<html lang="en">
<head>
<meta charset="utf-8">
<title>Singular REST Commander</title>
<link href="https://fonts.googleapis.com/css?family=Lato:400,700" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
</head>
<body>
<header>
<div class="logo">
<img src="https://static.wixstatic.com/media/7e7b4f_bd1a8a2e023c4329880c7bb12994f1fd~mv2.png/v1/crop/x_0,y_71,w_1578,h_155/fill/w_205,h_23,al_c,usm_0.66_1.00_0.01/7e7b4f_bd1a8a2e023c4329880c7bb12994f1fd~mv2.png" width="150" alt="Singular_Logo" title="">
</div>
<div class="logo2">
<a href="https://www.singular.live/">
<img src="https://static.wixstatic.com/media/7e7b4f_3ca9325e528747baabaa13dd349ad13b~mv2.png/v1/crop/x_83,y_0,w_580,h_517/fill/w_157,h_121,al_c,usm_0.66_1.00_0.01/7e7b4f_3ca9325e528747baabaa13dd349ad13b~mv2.png" width="30px">
</a>
</div>
</header>
<div class="container">
<div class="wrapper">
<div class="box box1">
<a id="btnSend" onclick='sendRestCmd()' class="variantbutton">SEND<i class="fa fa-paper-plane" ></i></a>
<a id="btnClear" onclick='clearUI()' class="variantbutton">CLEAR<i class="fa fa-undo"></i></a>
</div>
<div class="box box2">
<div class="url-title">URL</div>
<textarea id="requestURL" class="url">https://www.singular.live / ID: requestURL</textarea>
</div>
<div class="box box3">
<div>
<form>
<select id="selectRestCmd" onchange="selectRestCmdChange()" size="8">
<option style="background-color: #ADD136; color: #000; border-radius:0px" disabled>Select Function</option>
<!-- Options will be insert here by the Script -->
</select>
</form>
</div>
</div>
<div class="box box4">
<div class="boxheader">
<p>Request</p>
</div>
<textarea id="resourceURL" style="padding-left:7px; font-family: monospace;">Request Text... / ID = resourceURL</textarea>
</div>
<div class="box box5">
<div class="boxheader">
<p>Response</p>
</div>
<textarea id="responseStatus" style="padding-left:7px; font-family: monospace;">Response Text... / ID = responseStatus</textarea>
</div>
<div class="box box6">
<div class="boxheader">
<p>Request Body</p>
</div>
<textarea id="requestBody" style="padding-left:7px; font-family: monospace;">This is the area for JSON text, you can edit it. ID: requestBody</textarea>
</div>
<div class="box box7">
<div class="boxheader">
<p>JSON Response</p>
</div>
<textarea id="responseJSON" style="padding-left:7px; font-family: monospace;" readonly>And this area here is for the LOG text, it is set to read only. ID: responseJSON</textarea>
</div>
<div class="box box8">
<div>
<iframe class="iFrame" src="https://app.singular.live/output/35eUbv7rQO1zHGinkR0OWy/Output?aspect=16:9" id="appframe" src=""></iframe>
</div>
</div>
<div class="box box9">
<div class="boxheader">
<p>Debug Messages:</p>
</div>
<!-- debug output -->
<textarea id="logMessage" style="overflow-y: scroll; padding-left:7px; font-family: monospace;" readonly> ... </textarea>
</div>
</div>
</div>
</body>
</html>
@charset "utf-8";
/* CSS Document */
body {
margin: 0;
padding: 0;
font-family: "Lato";
color: #383838;
background: #f2f2f2;
}
a {
text-decoration: none;
color: #333333;
}
p {
color: #333333;
}
h3 {
color: black;
font-weight: 100;
font-size: 13px;
margin: 0px;
padding: 13px;
font-family: "Lato";
}
h2 {
color: black;
font-weight: 100;
margin-left: 210px;
margin-top: 60px;
}
td {
text-align: right;
}
.container {
width: 98%;
margin: auto;
}
header {
display: grid;
grid-auto-columns: 1fr 7fr 1fr;
background: #add136;
margin-bottom: 20px;
overflow: hidden;
text-align: center;
box-shadow: 0px 5px 15px #ccc;
}
.logo {
float: left;
margin-left: 30px;
padding: 7px;
grid-column: 1/2;
}
.logo2 {
float: right;
position: relative;
padding-top: 2px;
grid-column: 3/4;
}
.logo,
.logo2 {
margin: 4px 10px;
}
textarea {
resize: none;
font-family: Lato;
border-bottom-left-radius: 7px;
border-bottom-right-radius: 7px;
font-size: 10px;
padding-top: 5px;
}
.buttons {
padding-top: 30px;
text-align: center;
line-height: normal;
color: #333333;
}
.iFrame {
background-color: black;
width: 100%;
height: 100%;
border: none;
margin-top: 0px;
margin-left: 0px;
position: absolute;
}
#requestBody , #responseJSON, #logMessage{
height: 275px;
width: 100%;
}
#resourceURL , #responseStatus {
width: 100%;
height: 75px;
}
.variantbutton {
font-family: "Lato";
background-color: #add136;
color: #333333;
text-align: center;
text-decoration: none;
border-radius: 5px;
cursor: default;
align-self: center;
padding: 10px 0px;
font-size: 12px;
}
.fa {
padding-left: 5px;
}
#btnSend {
grid-column: 1/2;
}
#btnClear {
grid-column: 3/4;
}
.variantbutton:hover {
background-color: gray;
}
.url-title {
background-color: #add136;
border-bottom-left-radius: 7px;
border-top-left-radius: 7px;
align-self: center;
padding: 10px 5px;
font-size: 12px;
}
.url {
border-radius: 0px 7px 7px 0px;
resize: none;
overflow: hidden;
width: 100%;
padding: 8px;
font-size: 12px;
}
#logOutput {
width: 100%;
}
/*GRID-----*/
/*---------*/
/*---------*/
.wrapper {
display: grid;
grid-template-columns: 200px 1fr 1fr 1fr 1fr;
grid-template-rows: 35px 100px 300px 300px;
grid-gap: 7px;
/* justify-items:stretch; */
/* align-items:stretch; */
}
.wrapper > div {
padding: 0px;
}
.box1 {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
filter: drop-shadow(5px 1px 4px #cccccc);
border-radius: 7px;
z-index: 1;
grid-row: 1/2;
}
.box2 {
grid-column: 2/6;
display: grid;
grid-template-columns: 45px 1fr;
filter: drop-shadow(5px 1px 4px #cccccc);
border-radius: 7px;
}
.box3 {
grid-column: 1/2;
grid-row: 2/5;
filter: drop-shadow(5px 1px 4px #cccccc);
border-radius: 7px;
}
.box4 {
grid-column: 2/4;
grid-row: 2/3;
filter: drop-shadow(5px 1px 4px #cccccc);
border-radius: 7px;
}
.box5 {
grid-column: 4/6;
grid-row: 2/3;
filter: drop-shadow(5px 1px 4px #cccccc);
border-radius: 7px;
}
.box6 {
grid-column: 2/4;
grid-row: 3/4;
filter: drop-shadow(5px 1px 4px #cccccc);
border-radius: 7px;
}
.box7 {
grid-column: 4/6;
grid-row: 3/4;
filter: drop-shadow(5px 1px 4px #cccccc);
border-radius: 7px;
}
.box8 {
grid-column: 2/4;
grid-row: 4/5;
filter: drop-shadow(5px 1px 4px #cccccc);
border-radius: 7px;
}
.box9 {
grid-column: 4/6;
grid-row: 4/5;
filter: drop-shadow(5px 1px 4px #cccccc);
border-radius: 7px;
}
.nestedMain {
display: grid;
grid-template-rows: 25px auto;
grid-gap: 0px;
}
.nestedButtons {
display: grid;
grid-template-rows: 45px 45px;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-gap: 7px;
}
.box {
color: black;
}
.boxheader {
background: #add136;
border-top-left-radius: 7px;
border-top-right-radius: 7px;
grid-row: 1;
}
.boxheader > p {
font-size: 11px;
padding: 5px 10px;
font-weight: bolder;
height: 15px;
margin: 0px;
}
/*LISTBOX*/
/*-------*/
/*-------*/
form {
margin: 0;
height: 715px;
}
#selectRestCmd {
overflow-y: scroll;
border-radius: 7px;
width: 100%;
height: 100%;
}
#selectRestCmd > option:first-child {
width: 100%;
height: 17px;
padding: 5px 10px;
border-radius: 7px 0px 0px 0px;
font-size: 12px;
background-color: #ADD136;
color: #000;
}
#selectRestCmd > option {
width: 100%;
height: 17px;
padding: 7px 3px 3px 3px;
border-top: solid 1px #ccc;
border-width: 80%;
font-size: 10px;
align-self: center;
align-content: center;
align-items: center;
}
/*Without the "Select Function" - Header*/
/*
#mySelect > option:first-child{
border-top-left-radius: 7px;
border: none;
}
*/
/*With the "Select Function" Header*/
#selectRestCmd > option:first-child:hover {
border-right: solid 3px #add136;
}
#selectRestCmd > option:last-child {
border-bottom-left-radius: 7px;
}
#selectRestCmd > option:hover {
background-color: rgba(0, 0, 0, 0.1);
border-right: solid 3px green;
width: calc(100% - 9px);
margin-left: 0;
}
/*SCROLLBAR*/
body ::-webkit-scrollbar {
width: 6px;
border-bottom-right-radius: 7px;
border-top-right-radius: 7px;
}
body ::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
border-bottom-right-radius: 7px;
border-top-right-radius: 7px;
}
body ::-webkit-scrollbar-thumb {
background-color: darkgrey;
outline: 1px solid slategrey;
border-bottom-right-radius: 7px;
border-top-right-radius: 7px;
}
var restApiCalls = [];
var restApiCallMap = new Map();
var appInstanceId = '36766';
var dataNodeId = '1232';
var appAccessToken = '2CZziI55PpYD96bLPCvzon';
var credentials = 'YXBpc2RrQHNpbmd1bGFyLmxpdmU6c0RrQXBJ';
var urlSingularHome = 'https://app.singular.live';
var urlResourceAppInstances = '/apiv1/appinstances/';
var urlResourceDataNodes = '/apiv1/datanodes/';
var urlAppInstances = urlSingularHome + urlResourceAppInstances;
var urlDataNodes = urlSingularHome + urlResourceDataNodes;
//var urlAppId = "https://app.singular.live/apiv1/appinstances/8963/control";
//var urlAccessToken = "https://beta.singular.live/apiv1/control/3Wqax3fo6qXs0f7cASz61Y";
var requestMethod;
var requestURL;
var resourceURL;
var requestBody;
var plUpCtrlNode1 = [{
"compositionName": "Baseline",
"controlNode": {
"payload": {
"Title": "Title 1",
"Text": "Baseline Information 1"
}
}
}];
var plUpCtrlNode2 = [{
"compositionName": "Baseline",
"controlNode": {
"payload": {
"Title": "Title 2",
"Text": "Baseline Information 2"
}
}
}];
var plAnimIn = [{
"compositionName": "Baseline",
"animation": {
"action": "play",
"from": "Out",
"to": "In"
}
}];
var plAnimOUT = [{
"compositionName": "Baseline",
"animation": {
"action": "play",
"from": "In",
"to": "Out"
}
}];
var plEmpty = [{}];
/**************************************************************************
* initialize
*/
$(document).ready(function () {
logOutput("[ready]: Start ...");
// App Instance API Calls
restApiCallMap.set('GET All App Instances', {
'method': "GET",
'resourceURL': urlResourceAppInstances,
'requestURL': urlAppInstances,
'requestBody': null
});
restApiCallMap.set('GET One App Instance', {
'method': "GET",
'resourceURL': urlResourceAppInstances + '{app_instance_id}',
'requestURL': urlAppInstances + appInstanceId,
'requestBody': null
});
restApiCallMap.set('GET Outputs of App Instance', {
'method': "GET",
'resourceURL': urlResourceAppInstances + '{app_instance_id}' + '/outputs',
'requestURL': urlAppInstances + appInstanceId + '/outputs',
'requestBody': null
});
restApiCallMap.set('GET Control Properties of App Instance', {
'method': "GET",
'resourceURL': urlResourceAppInstances + '{app_instance_id}' + '/control',
'requestURL': urlAppInstances + appInstanceId + '/control',
'requestBody': null
});
restApiCallMap.set('GET Subcompositions of an App Instance', {
'method': "GET",
'resourceURL': urlResourceAppInstances + '{app_instance_id}' + '/subcompositions',
'requestURL': urlAppInstances + appInstanceId + '/subcompositions',
'requestBody': null
});
restApiCallMap.set('GET Subcompositions of an App Instance', {
'method': "GET",
'resourceURL': urlResourceAppInstances + '{app_instance_id}' + '/subcompositions',
'requestURL': urlAppInstances + appInstanceId + '/subcompositions',
'requestBody': null
});
restApiCallMap.set('PUT Load "Blue" Composition into an App Instance', {
'method': "PUT",
'resourceURL': urlResourceAppInstances + '{app_instance_id}' + '/composition',
'requestURL': urlAppInstances + appInstanceId + '/composition',
'requestBody': {
"refId": "85834"
}
});
restApiCallMap.set('PUT Load "Green" Composition into an App Instance', {
'method': "PUT",
'resourceURL': urlResourceAppInstances + '{app_instance_id}' + '/composition',
'requestURL': urlAppInstances + appInstanceId + '/composition',
'requestBody': {
"refId": "85836"
}
});
restApiCallMap.set('PUT Jump Animation "Panel Left" to state "In"', {
'method': "PUT",
'resourceURL': urlResourceAppInstances + '{app_instance_id}' + '/control',
'requestURL': urlAppInstances + appInstanceId + '/control',
'requestBody': [{
"compositionName": "Panel Left",
"animation": {
"action": "jump",
"to": "In"
}
}]
});
restApiCallMap.set('PUT Play Animation "Lower Topic" to state "In"', {
'method': "PUT",
'resourceURL': urlResourceAppInstances + '{app_instance_id}' + '/control',
'requestURL': urlAppInstances + appInstanceId + '/control',
'requestBody': [{
"compositionName": "Lower Topic",
"animation": {
"action": "play",
"to": "In"
}
}]
});
restApiCallMap.set('PUT Play Animation "Lower Line" to state "In"', {
'method': "PUT",
'resourceURL': urlResourceAppInstances + '{app_instance_id}' + '/control',
'requestURL': urlAppInstances + appInstanceId + '/control',
'requestBody': [{
"compositionName": "Lower Line",
"animation": {
"action": "play",
"to": "In"
}
}]
});
restApiCallMap.set('PUT Play Animation "Baseline Crawl" to state "In"', {
'method': "PUT",
'resourceURL': urlResourceAppInstances + '{app_instance_id}' + '/control',
'requestURL': urlAppInstances + appInstanceId + '/control',
'requestBody': [{
"compositionName": "Baseline Crawl",
"animation": {
"action": "play",
"to": "In"
}
}]
});
restApiCallMap.set('PUT Play Animation All to state "Out"', {
'method': "PUT",
'resourceURL': urlResourceAppInstances + '{app_instance_id}' + '/control',
'requestURL': urlAppInstances + appInstanceId + '/control',
'requestBody': [{
"compositionName": "Lower Line",
"animation": {
"action": "play",
"to": "Out"
}
}, {
"compositionName": "Lower Topic",
"animation": {
"action": "play",
"to": "Out"
}
}, {
"compositionName": "Panel Left",
"animation": {
"action": "play",
"to": "Out"
}
}, {
"compositionName": "Baseline Crawl",
"animation": {
"action": "play",
"to": "Out"
}
}]
});
restApiCallMap.set('PUT Update "Lower Topic" Content', {
'method': "PUT",
'resourceURL': urlResourceAppInstances + '{app_instance_id}' + '/control',
'requestURL': urlAppInstances + appInstanceId + '/control',
'requestBody': [{
"compositionName": "Lower Topic",
"controlNode": {
"payload": {
"Title": "Singular REST Commander",
"Subtitle": "Content updated using REST API PUT method..."
}
}
}]
});
restApiCallMap.set('PUT Update "Lower Line" Content', {
'method': "PUT",
'resourceURL': urlResourceAppInstances + '{app_instance_id}' + '/control',
'requestURL': urlAppInstances + appInstanceId + '/control',
'requestBody': [{
"compositionName": "Lower Line",
"controlNode": {
"payload": {
"Text": "Singular.live - Graphics built for Streaming"
}
}
}]
});
restApiCallMap.set('POST Create App Instance (min specs)', {
'method': "POST",
'resourceURL': urlResourceAppInstances,
'requestURL': urlAppInstances,
'requestBody': {
"folder": "4e867728-b556-4d10-8761-afecf431614b",
"apptemplate_id": "57",
"apptemplate_version_status": "published",
"name": "New Studio Control (no composition specified)"
}
});
restApiCallMap.set('POST Create App Instance (full specs)', {
'method': "POST",
'resourceURL': urlResourceAppInstances,
'requestURL': urlAppInstances,
'requestBody': {
"folder": "4e867728-b556-4d10-8761-afecf431614b",
"apptemplate_id": "57",
"apptemplate_version_status": "published",
"name": "New Studio Control (composition specified)",
"compositionList": [{
"name": "Comp-for-DevDocs",
"composition": {
"refId": "85834"
}
}],
"account_id": "56",
"user_id": "2290",
"shareurl": "0"
}
});
restApiCallMap.set('DELETE Delete App Instance', {
'method': "DELETE",
'resourceURL': urlResourceAppInstances + '{app_instance_id}',
'requestURL': urlAppInstances + '39453',
'requestBody': null
});
// Data Node API Calls
restApiCallMap.set('GET All Data Nodes', {
'method': "GET",
'resourceURL': urlResourceDataNodes,
'requestURL': urlDataNodes,
'requestBody': null
});
restApiCallMap.set('GET Single Data Node', {
'method': "GET",
'resourceURL': urlResourceDataNodes + '{datanode_id}',
'requestURL': urlDataNodes + dataNodeId,
'requestBody': null
});
restApiCallMap.set('GET Single Data Node Definition', {
'method': "GET",
'resourceURL': urlResourceDataNodes + '{datanode_id}' + '/data',
'requestURL': urlDataNodes + dataNodeId + '/data',
'requestBody': null
});
restApiCallMap.set('PUT Update Data Node', {
'method': "PUT",
'resourceURL': urlResourceDataNodes + '{datanode_id}' + '/data',
'requestURL': urlDataNodes + dataNodeId + '/data',
'requestBody': {
"payload": {
"Crawl Title": "Title text here...",
"Crawl Text": "Crawl text here...",
"Crawl Speed": "10.0"
}
}
});
// listApiCalls(restApiCallMap);
initCommandSelector(restApiCallMap);
logOutput("[ready]: ... End");
});
/**************************************************************************
*
*/
function initCommandSelector(callMap) {
var x = document.getElementById("selectRestCmd");
for (var [key, value] of callMap) {
// logOutput("[initCommandSelector]: [key] = [" + key + "] [value] = [" + JSON.stringify(value) + "]");
var option = document.createElement("option");
option.text = key;
x.add(option);
}
}
/**************************************************************************
*
*/
function selectRestCmdChange() {
var x = document.getElementById("selectRestCmd");
if (x.selectedIndex >= 0) {
var sel = x.options[x.selectedIndex];
logOutput("[selectRestCmdChange]: [sel.text] = [" + sel.text + "]");
requestMethod = restApiCallMap.get(sel.text).method;
requestURL = restApiCallMap.get(sel.text).requestURL;
resourceURL = restApiCallMap.get(sel.text).resourceURL;
requestBody = restApiCallMap.get(sel.text).requestBody;
document.getElementById("requestURL").innerHTML = requestURL;
document.getElementById("resourceURL").innerHTML = requestMethod + ': ' + resourceURL;
document.getElementById("requestBody").innerHTML = JSON.stringify(requestBody, undefined, 2);
document.getElementById("responseStatus").innerHTML = '';
document.getElementById("responseJSON").innerHTML = '';
}
}
/**************************************************************************
*
*/
function sendRestCmd() {
var x = document.getElementById("selectRestCmd");
if (x.selectedIndex >= 0) {
var sel = x.options[x.selectedIndex];
logOutput("[sendRestCmd]: [sel.text] = [" + sel.text + "]");
requestURL = document.getElementById("requestURL").value;
requestBody = JSON.parse(document.getElementById("requestBody").value);
switch (requestMethod) {
case "GET":
// singularRestGet(requestURL, credentials);
singularRestCommand(requestURL, requestMethod, requestBody, credentials);
break;
case "PUT":
singularRestCommand(requestURL, requestMethod, requestBody, credentials);
break;
case "POST":
singularRestCommand(requestURL, requestMethod, requestBody, credentials);
break;
case "PATCH":
// singularRestPatch(requestURL, requestBody, credentials);
break;
case "DELETE":
singularRestCommand(requestURL, requestMethod, requestBody, credentials);
break;
default:
logOutput("[sendRestCmd]: UNKNOWN [restRequest] = [" + restRequest + "]");
}
}
}
/**************************************************************************
* listApiCalls
*/
function clearUI() {
document.getElementById("requestURL").innerHTML = '';
document.getElementById("resourceURL").innerHTML = '';
document.getElementById("requestBody").innerHTML = '';
document.getElementById("responseStatus").innerHTML = '';
document.getElementById("responseJSON").innerHTML = '';
document.getElementById("logMessage").innerHTML = '';
}
/**************************************************************************
* listApiCalls
*/
function listApiCalls(callMap) {
for (var [key, value] of callMap) {
logOutput("[listApiCalls]: [key] = [" + key + "] [value] = [" + JSON.stringify(value) + "]");
}
}
/**************************************************************************
* logOutput
*/
function logOutput(message) {
var logText = document.getElementById("logMessage").innerHTML;
document.getElementById("logMessage").innerHTML = logText + "\n" + message;
}
/**************************************************************************
* singularRestCommand
*/
function singularRestCommand(url, method, body, auth) {
var responseMessage;
var options;
// setup the HTTP call
if (method == 'GET' || method == 'DELETE') {
options = {
'method': method,
'contentType': 'application/json',
'mode': 'cors',
// 'muteHttpExceptions': true,
"headers": {
"Authorization": "Basic " + auth
}
}
} else if (method == 'PUT' || method == 'POST') {
options = {
'method': method,
'mode': 'cors',
"headers": {
"Authorization": "Basic " + auth,
'content-type': 'application/json'
},
'body': JSON.stringify(body)
};
}
fetch(url, options)
.then((httpResponse) => {
if (httpResponse.ok) {
// logOutput(httpResponse.status + "\n" + httpResponse.ok + "\n" + httpResponse.statusText);
responseMessage = httpResponse.status + " " + httpResponse.ok + " " + httpResponse.statusText;
document.getElementById("responseStatus").innerHTML = responseMessage;
if (method != 'PUT') {
return httpResponse.json();
}
} else {
responseMessage = httpResponse.status + " " + httpResponse.ok + " " + httpResponse.statusText;
document.getElementById("responseStatus").innerHTML = responseMessage;
return Promise.reject("Fetch did not succeed");
}
})
.then(json => {
console.log(json)
document.getElementById("responseJSON").innerHTML = JSON.stringify(json, undefined, 2);
})
.catch((err) => {
logOutput("ERROR: " + err);
console.log("ERROR: " + err);
});
}
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.