11package it .eng .knowage .engine .api .excel .export .dashboard ;
22
33import it .eng .knowage .commons .multitenant .OrganizationImageManager ;
4+ import it .eng .knowage .commons .security .PathTraversalChecker ;
45import it .eng .knowage .engine .api .excel .export .IWidgetExporter ;
56import it .eng .knowage .engine .api .excel .export .dashboard .exporters .DashboardWidgetExporterFactory ;
67import it .eng .spagobi .commons .SingletonConfig ;
1213import it .eng .spagobi .tenant .TenantManager ;
1314import it .eng .spagobi .utilities .exceptions .SpagoBIRuntimeException ;
1415import lombok .Getter ;
16+ import org .apache .commons .codec .binary .Base64 ;
1517import org .apache .commons .lang3 .StringUtils ;
1618import org .apache .logging .log4j .LogManager ;
1719import org .apache .logging .log4j .Logger ;
3032import org .json .JSONException ;
3133import org .json .JSONObject ;
3234
33- import java .io .ByteArrayOutputStream ;
34- import java .io .IOException ;
35+ import javax .ws .rs .core .UriBuilder ;
36+ import java .io .*;
37+ import java .net .URI ;
38+ import java .nio .file .Files ;
39+ import java .nio .file .Path ;
40+ import java .nio .file .Paths ;
3541import java .text .DateFormat ;
3642import java .text .SimpleDateFormat ;
3743import java .util .*;
3844
3945import static it .eng .knowage .engine .api .excel .export .dashboard .StaticLiterals .EXCEL_ERROR ;
46+ import static java .nio .charset .StandardCharsets .UTF_8 ;
4047import static org .apache .poi .ss .usermodel .DataConsolidateFunction .*;
4148
4249public class DashboardExcelExporter extends Common {
@@ -51,6 +58,15 @@ public class DashboardExcelExporter extends Common {
5158 private final JSONObjectUtils jsonObjectUtils ;
5259 private final DatastoreUtils datastoreUtils ;
5360
61+ // SCHEDULER
62+ private static final String CONFIG_NAME_FOR_EXPORT_SCRIPT_PATH = "internal.nodejs.chromium.export.path" ;
63+ private static final String SCRIPT_NAME = "cockpit-export-xls.js" ;
64+ private String role ;
65+ private String userUniqueIdentifier = "" ;
66+ private String requestURL = "" ;
67+ private String organization = "" ;
68+
69+
5470 private String imageB64 = "" ;
5571 private static final String DOCUMENT_NAME = "" ;
5672 private final boolean isSingleWidgetExport ;
@@ -67,7 +83,19 @@ public DashboardExcelExporter(DatastoreUtils datastoreUtils, JSONObject body) {
6783 locale = getLocaleFromBody (body );
6884 jsonObjectUtils = new JSONObjectUtils ();
6985 styleProvider = new StyleProvider (jsonObjectUtils );
86+ }
7087
88+ public DashboardExcelExporter (DatastoreUtils datastoreUtils , JSONObject body , String role , String userId , String requestUrl , String organization ) {
89+ this .datastoreUtils = datastoreUtils ;
90+ this .isSingleWidgetExport = body .optBoolean ("exportWidget" );
91+ this .body = body ;
92+ locale = getLocaleFromBody (body );
93+ jsonObjectUtils = new JSONObjectUtils ();
94+ styleProvider = new StyleProvider (jsonObjectUtils );
95+ this .role = role ;
96+ this .userUniqueIdentifier = userId ;
97+ this .requestURL = requestUrl ;
98+ this .organization = organization ;
7199 }
72100
73101 private Locale getLocaleFromBody (JSONObject body ) {
@@ -81,7 +109,105 @@ private Locale getLocaleFromBody(JSONObject body) {
81109 }
82110
83111 }
112+ public byte [] getScheduledBinaryData (String documentLabel ) throws IOException , InterruptedException {
113+ try {
114+ final Path outputDir = Files .createTempDirectory ("knowage-xls-exporter-" );
115+
116+ String encodedUserId = Base64 .encodeBase64String (userUniqueIdentifier .getBytes (UTF_8 ));
117+ // Script
118+ String cockpitExportScriptPath = SingletonConfig .getInstance ()
119+ .getConfigValue (CONFIG_NAME_FOR_EXPORT_SCRIPT_PATH );
120+ Path exportScriptFullPath = Paths .get (cockpitExportScriptPath , SCRIPT_NAME );
121+
122+ if (!Files .isRegularFile (exportScriptFullPath )) {
123+ String msg = String .format (
124+ "Cannot find export script at \" %s\" : did you set the correct value for %s configuration?" ,
125+ exportScriptFullPath , CONFIG_NAME_FOR_EXPORT_SCRIPT_PATH );
126+ IllegalStateException ex = new IllegalStateException (msg );
127+ LOGGER .error (msg , ex );
128+ throw ex ;
129+ }
130+
131+ URI url = UriBuilder .fromUri (requestURL )
132+ .replaceQueryParam ("outputType_description" , "HTML" )
133+ .replaceQueryParam ("outputType" , "HTML" )
134+ .replaceQueryParam ("role" , role )
135+ .replaceQueryParam ("organization" , organization )
136+ .build ();
137+
138+ // avoid sonar security hotspot issue
139+ String cockpitExportExternalProcessName = SingletonConfig .getInstance ()
140+ .getConfigValue ("KNOWAGE.DASHBOARD.EXTERNAL_PROCESS_NAME" );
141+ LOGGER .info ("CONFIG label=\" KNOWAGE.DASHBOARD.EXTERNAL_PROCESS_NAME\" : " + cockpitExportExternalProcessName );
142+
143+ String stringifiedRequestUrl = url .toString ();
144+ // replace localhost:8080 with 127.0.0.1:3000
145+ // stringifiedRequestUrl = stringifiedRequestUrl.replace("localhost:8080", "127.0.0.1:3000");
146+
147+ ProcessBuilder processBuilder = new ProcessBuilder (cockpitExportExternalProcessName , exportScriptFullPath .toString (),
148+ encodedUserId , outputDir .toString (), stringifiedRequestUrl );
149+
150+ setWorkingDirectory (cockpitExportScriptPath , processBuilder );
151+
152+ LOGGER .info ("Node complete command line: {}" , processBuilder .command ());
153+
154+ LOGGER .info ("Starting export script" );
155+ Process exec = processBuilder .start ();
156+
157+ logOutputToCoreLog (exec );
158+
159+ LOGGER .info ("Waiting..." );
160+ exec .waitFor ();
161+ LOGGER .warn ("Exit value: {}" , exec .exitValue ());
162+
163+ // the script creates the resulting xls and saves it to outputFile
164+ Path outputFile = PathTraversalChecker .get (outputDir .toString (), documentLabel + ".xlsx" ).toPath ();
165+ return getByteArrayFromFile (outputFile , outputDir );
166+ } catch (Exception e ) {
167+ LOGGER .error ("Error during scheduled export execution" , e );
168+ throw e ;
169+ }
170+ }
171+ private byte [] getByteArrayFromFile (Path excelFile , Path outputDir ) {
172+ String fileName = excelFile .toString ();
173+
174+ try (FileInputStream fis = new FileInputStream (fileName );
175+ ByteArrayOutputStream bos = new ByteArrayOutputStream ()) {
176+ byte [] buf = new byte [1024 ];
177+ for (int readNum ; (readNum = fis .read (buf )) != -1 ; ) {
178+ // Writes len bytes from the specified byte array starting at offset off to this byte array output stream
179+ bos .write (buf , 0 , readNum ); // no doubt here is 0
180+ }
181+ return bos .toByteArray ();
182+ } catch (Exception e ) {
183+ LOGGER .error ("Cannot serialize excel file" , e );
184+ throw new SpagoBIRuntimeException ("Cannot serialize excel file" , e );
185+ } finally {
186+ try {
187+ if (Files .isRegularFile (excelFile )) {
188+ Files .delete (excelFile );
189+ }
190+ Files .delete (outputDir );
191+ } catch (Exception e ) {
192+ LOGGER .error ("Cannot delete temp file" , e );
193+ }
194+ }
195+ }
196+ private void logOutputToCoreLog (Process exec ) throws IOException {
197+ InputStreamReader isr = new InputStreamReader (exec .getInputStream ());
198+ BufferedReader b = new BufferedReader (isr );
199+ String line = null ;
200+ LOGGER .warn ("Process output" );
201+ while ((line = b .readLine ()) != null ) {
202+ LOGGER .warn (line );
203+ }
204+ }
84205
206+ private void setWorkingDirectory (String cockpitExportScriptPath , ProcessBuilder processBuilder ) {
207+ // Required by puppeteer v19
208+ processBuilder .directory (new File (cockpitExportScriptPath ));
209+
210+ }
85211 public byte [] getDashboardBinaryData (JSONObject body , boolean isDashboardSingleWidgetExport ) {
86212
87213 if (body == null ) {
0 commit comments