Skip to content

Commit 4413475

Browse files
Base64-encoded file serving for web app, finally 🎉
1 parent 45d21c6 commit 4413475

File tree

8 files changed

+144
-9
lines changed

8 files changed

+144
-9
lines changed

gulpfile.babel.js

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,45 @@ import to5ify from "6to5ify";
77
import streamify from "gulp-streamify";
88
import source from "vinyl-source-stream";
99
import scss from "gulp-sass";
10+
import fs from "fs";
11+
import mime from "mime-types";
1012
import pkg from "./package.json";
1113

1214
const
15+
APP_NAME = `EntityBrowser`,
1316
SOURCE_DIR = `${ __dirname }/src`,
1417
BUILD_DIR = `${ __dirname }/build`,
18+
STATIC_DATA_FILE = `${ SOURCE_DIR }/cls/${ APP_NAME }/REST/StaticData.cls`,
1519
context = {
1620
package: pkg
1721
};
1822

23+
function getAllFiles (dir) {
24+
let results = [];
25+
let list = fs.readdirSync(dir);
26+
list.forEach(function(file) {
27+
file = dir + '/' + file;
28+
let stat = fs.statSync(file);
29+
if (stat && stat.isDirectory()) results = results.concat(getAllFiles(file));
30+
else results.push(file)
31+
});
32+
return results;
33+
}
34+
35+
function base64Encode (file) {
36+
return (new Buffer(fs.readFileSync(file, 'binary'), 'binary')).toString(`base64`);
37+
}
38+
1939
gulp.task("clean", () => {
2040
return gulp.src(BUILD_DIR, { read: false })
2141
.pipe(rimraf());
2242
});
2343

2444
gulp.task("cls", ["clean"], () => {
25-
return gulp.src(SOURCE_DIR + "/cls/**/*.cls")
45+
return gulp.src([
46+
`!${ STATIC_DATA_FILE }`,
47+
`${ SOURCE_DIR }/cls/**/*.cls`
48+
])
2649
.pipe(preprocess({ context: context }))
2750
.pipe(gulp.dest(BUILD_DIR + "/cls"));
2851
});
@@ -60,4 +83,21 @@ gulp.task("css", ["clean"], () => {
6083
.pipe(gulp.dest(`${ BUILD_DIR }/static/css`));
6184
});
6285

63-
gulp.task("default", ["cls", "html", "js", "css", "etc"]);
86+
/// doing file replacement manually because preprocess sucks.
87+
gulp.task("StaticData", ["html", "js", "css", "etc"], () => {
88+
let files = getAllFiles(`${ BUILD_DIR }/static`),
89+
staticData = files.map((fileName, i) =>
90+
`/// ${ fileName.replace(`${ BUILD_DIR }/static/`, "") }\r\n\
91+
XData File${ i } [ MimeType = ${ mime.lookup(fileName) || "text/plain" } ]\r\n\
92+
{\r\n\
93+
${ base64Encode(fileName).replace(/(.{32765})/g, "$1\r\n") }\r\n\
94+
}`
95+
).join(`\r\n\r\n`);
96+
fs.writeFileSync(
97+
STATIC_DATA_FILE.replace(SOURCE_DIR, BUILD_DIR),
98+
new Buffer(fs.readFileSync(STATIC_DATA_FILE)).toString()
99+
.replace("<!-- staticData -->", staticData)
100+
);
101+
});
102+
103+
gulp.task("default", ["cls", "StaticData"]);

import.cmd

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,18 @@ set CACHE_DIR=C:\Program Files\InterSystems\Ensemble
1010
set NAMESPACE=SAMPLES
1111
:: Other variables
1212
set BUILD_DIR=build\cls
13-
set BUILD_STATIC_DIR=build\static
14-
set CSP_DIR=C:\Program Files\InterSystems\Ensemble\CSP\samples\EntityBrowser
13+
:: set BUILD_STATIC_DIR=build\static
14+
:: set CSP_DIR=C:\Program Files\InterSystems\Ensemble\CSP\samples\EntityBrowser
1515
:: User credentials. Remove if necessary.
1616
set USERNAME=_SYSTEM
1717
set PASSWORD=SYS
1818

1919
:: Build and import application to Caché
2020
echo Importing project...
2121
call npm run gulp
22-
call xcopy /sy "%~dp0\%BUILD_STATIC_DIR%" "%CSP_DIR%"
22+
:: call xcopy /sy "%~dp0\%BUILD_STATIC_DIR%" "%CSP_DIR%"
2323
(
2424
echo %USERNAME%
2525
echo %PASSWORD%
26-
echo zn "SAMPLES" set st = $system.Status.GetErrorText($system.OBJ.ImportDir("%~dp0%BUILD_DIR%",,"ck /checkuptodate=all",,1^^^)^^^) w "IMPORT STATUS: "_$case(st="",1:"OK",:st^^^), ! halt
26+
echo zn "%NAMESPACE%" set st = $system.Status.GetErrorText($system.OBJ.ImportDir("%~dp0%BUILD_DIR%",,"ck",,1^^^)^^^) w "IMPORT STATUS: "_$case(st="",1:"OK",:st^^^), ! halt
2727
) | "%CACHE_DIR%\bin\cache.exe" -s "%CACHE_DIR%\mgr" -U %NAMESPACE%

import.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,6 @@ cp -R "$DIR/$BUILD_STATIC_DIR"* "$CSP_DIR"
2525
cat <<EOT | "$CACHE_DIR/bin/cache" -s "$CACHE_DIR/mgr" -U $NAMESPACE
2626
$USERNAME
2727
$PASSWORD
28-
set st = \$system.Status.GetErrorText(\$system.OBJ.ImportDir("$DIR/$BUILD_DIR",,"ck /checkuptodate=all",,1))
28+
zn "$NAMESPACE" set st = \$system.Status.GetErrorText(\$system.OBJ.ImportDir("$DIR/$BUILD_DIR",,"ck /checkuptodate=all",,1))
2929
write "IMPORT STATUS: "_\$case(st="",1:"OK",:st), ! halt
3030
EOT

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "iknow-entity-browser",
3-
"version": "0.8.7",
3+
"version": "0.9.0",
44
"description": "Visualizer for iKnow entities",
55
"main": "gulpfile.babel.js",
66
"scripts": {
@@ -25,6 +25,7 @@
2525
"gulp-rimraf": "^0.2.0",
2626
"gulp-sass": "^3.1.0",
2727
"gulp-streamify": "^1.0.2",
28+
"mime-types": "^2.1.15",
2829
"vinyl-source-stream": "^1.1.0"
2930
}
3031
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@
9797
/// items:
9898
/// type: string
9999
/// </example>
100-
Class EntityBrowser.Router Extends (%iKnow.REST.Base, %iKnow.REST.Utils)
100+
Class EntityBrowser.REST.API Extends (%iKnow.REST.Base, %iKnow.REST.Utils)
101101
{
102102

103103
Parameter PAGESIZE = 0;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/// Application entry point.
2+
Class EntityBrowser.REST.Router Extends (ServeXData, StaticData)
3+
{
4+
5+
XData UrlMap
6+
{
7+
<Routes>
8+
<Map Prefix="/api" Forward="EntityBrowser.REST.API"/>
9+
<Route Url="/(.*)" Method="GET" Call="StaticDelivery"/>
10+
</Routes>
11+
}
12+
13+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/// Class that serves the static data encoded in Base64 and stored in this class in XData blocks.
2+
Class EntityBrowser.REST.ServeXData Extends %CSP.REST
3+
{
4+
5+
/// Index file name which will be served when requesting "/".
6+
Parameter IndexFile = "index.html";
7+
8+
/// Identifies the compilation time, which is used to form "Last-Modified" HTTP header.
9+
Parameter CompileTime = {"""" _ $zd($h, 11) _ ", "_ $zdt($NOW(0), 2,1) _ " GMT"""};
10+
11+
/// $LB of pairs $LB("fileName1", "XDataName1", "FileName2", ...)
12+
/// Generated in GenerateFiles generator.
13+
Parameter Files As CONFIGVALUE;
14+
15+
/// Copy the <Route> definition to the URLMap XData of your class.
16+
XData UrlMap
17+
{
18+
<Routes>
19+
<Route Url="/(.*)" Method="GET" Call="StaticDelivery"/>
20+
</Routes>
21+
}
22+
23+
/// A generator for Files parameter.
24+
ClassMethod FillFiles() As %Status [ CodeMode = objectgenerator, ForceGenerate ]
25+
{
26+
set list = $LB()
27+
set (ind, j) = 0
28+
for i=1:1:%compiledclass.XDatas.Count() {
29+
set xd = %compiledclass.XDatas.GetAt(i)
30+
set:(xd.Description = ..#IndexFile) ind = i
31+
continue:($FIND(xd.Name,"File")=0)
32+
set $LIST(list, j*2+1) = xd.Description
33+
set $LIST(list, j*2+2) = xd.Name
34+
set j = j + 1
35+
}
36+
if (ind '= 0) { // add index file
37+
set xd = %compiledclass.XDatas.GetAt(ind)
38+
set $LIST(list, j*2+1) = ""
39+
set $LIST(list, j*2+2) = xd.Name
40+
}
41+
do $system.OBJ.UpdateConfigParam("EntityBrowser.REST.ServeXData", "Files", list)
42+
return $$$OK
43+
}
44+
45+
ClassMethod GetXData(fileName As %String) As %Dictionary.CompiledXData
46+
{
47+
set xdn = ""
48+
for i=1:2:$LISTLENGTH(..#Files) {
49+
continue:($LISTGET(..#Files, i) '= fileName)
50+
set xdn = $LISTGET(..#Files, i + 1)
51+
}
52+
return:(xdn="") ""
53+
return ##class(%Dictionary.CompiledXData).%OpenId($ClassName()_"||"_xdn)
54+
}
55+
56+
ClassMethod StaticDelivery(file As %String) As %Status
57+
{
58+
set xdata = ..GetXData(file)
59+
return:(xdata = "") ..Http404()
60+
61+
set cTime = $PARAMETER($ClassName(), "CompileTime")
62+
set %response.ContentType = xdata.MimeType
63+
do %response.SetHeader("Last-Modified", cTime)
64+
if (%request.GetCgiEnv("HTTP_IF_MODIFIED_SINCE") = cTime) {
65+
set %response.Status = "304 Not Modified"
66+
return $$$OK
67+
}
68+
69+
write $System.Encryption.Base64Decode(xdata.Data.Read(xdata.Data.Size))
70+
71+
return $$$OK
72+
}
73+
74+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/// A class containing static web application.
2+
Class EntityBrowser.REST.StaticData [ GeneratedBy = ExternalTools ]
3+
{
4+
5+
<!-- staticData -->
6+
7+
}

0 commit comments

Comments
 (0)