mirror of
https://github.com/apache/nifi.git
synced 2025-02-06 01:58:32 +00:00
NIFI-13329 - Updating the standard content viewer to render an error message when there is an error formatting.
Co-authored-by: Pierre Villard <pierre.villard.fr@gmail.com> Signed-off-by: Pierre Villard <pierre.villard.fr@gmail.com> This closes #8905.
This commit is contained in:
parent
a21c2544ad
commit
48edbeed90
@ -57,6 +57,7 @@
|
||||
<targetPath>WEB-INF/jsp</targetPath>
|
||||
<includes>
|
||||
<include>codemirror.jsp</include>
|
||||
<include>format-error.jsp</include>
|
||||
</includes>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
|
@ -25,8 +25,9 @@ import org.apache.avro.generic.GenericData;
|
||||
import org.apache.avro.generic.GenericDatumReader;
|
||||
import org.apache.avro.io.DatumReader;
|
||||
import org.apache.nifi.web.ViewableContent.DisplayMode;
|
||||
import org.apache.nifi.xml.processing.ProcessingException;
|
||||
import org.apache.nifi.xml.processing.transform.StandardTransformProvider;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.yaml.snakeyaml.DumperOptions;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
@ -44,6 +45,8 @@ import java.util.Set;
|
||||
|
||||
public class StandardContentViewerController extends HttpServlet {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(StandardContentViewerController.class);
|
||||
|
||||
private static final Set<String> supportedMimeTypes = new HashSet<>();
|
||||
|
||||
static {
|
||||
@ -84,16 +87,15 @@ public class StandardContentViewerController extends HttpServlet {
|
||||
if (DisplayMode.Original.equals(content.getDisplayMode())) {
|
||||
formatted = content.getContent();
|
||||
} else {
|
||||
if ("application/json".equals(contentType)) {
|
||||
// format json
|
||||
final ObjectMapper mapper = new ObjectMapper();
|
||||
final Object objectJson = mapper.readValue(content.getContentStream(), Object.class);
|
||||
formatted = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(objectJson);
|
||||
} else if ("application/xml".equals(contentType) || "text/xml".equals(contentType)) {
|
||||
// format xml
|
||||
final StringWriter writer = new StringWriter();
|
||||
|
||||
try {
|
||||
try {
|
||||
if ("application/json".equals(contentType)) {
|
||||
// format json
|
||||
final ObjectMapper mapper = new ObjectMapper();
|
||||
final Object objectJson = mapper.readValue(content.getContentStream(), Object.class);
|
||||
formatted = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(objectJson);
|
||||
} else if ("application/xml".equals(contentType) || "text/xml".equals(contentType)) {
|
||||
// format xml
|
||||
final StringWriter writer = new StringWriter();
|
||||
final StreamSource source = new StreamSource(content.getContentStream());
|
||||
final StreamResult result = new StreamResult(writer);
|
||||
|
||||
@ -101,67 +103,69 @@ public class StandardContentViewerController extends HttpServlet {
|
||||
transformProvider.setIndent(true);
|
||||
|
||||
transformProvider.transform(source, result);
|
||||
} catch (final ProcessingException te) {
|
||||
throw new IOException("Unable to transform content as XML: " + te, te);
|
||||
}
|
||||
|
||||
// get the transformed xml
|
||||
formatted = writer.toString();
|
||||
} else if ("application/avro-binary".equals(contentType) || "avro/binary".equals(contentType) || "application/avro+binary".equals(contentType)) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append("[");
|
||||
// Use Avro conversions to display logical type values in human readable way.
|
||||
final GenericData genericData = new GenericData();
|
||||
genericData.addLogicalTypeConversion(new Conversions.DecimalConversion());
|
||||
genericData.addLogicalTypeConversion(new TimeConversions.DateConversion());
|
||||
genericData.addLogicalTypeConversion(new TimeConversions.TimeMicrosConversion());
|
||||
genericData.addLogicalTypeConversion(new TimeConversions.TimeMillisConversion());
|
||||
genericData.addLogicalTypeConversion(new TimeConversions.TimestampMicrosConversion());
|
||||
genericData.addLogicalTypeConversion(new TimeConversions.TimestampMillisConversion());
|
||||
genericData.addLogicalTypeConversion(new TimeConversions.LocalTimestampMicrosConversion());
|
||||
genericData.addLogicalTypeConversion(new TimeConversions.LocalTimestampMillisConversion());
|
||||
final DatumReader<GenericData.Record> datumReader = new GenericDatumReader<>(null, null, genericData);
|
||||
try (final DataFileStream<GenericData.Record> dataFileReader = new DataFileStream<>(content.getContentStream(), datumReader)) {
|
||||
while (dataFileReader.hasNext()) {
|
||||
final GenericData.Record record = dataFileReader.next();
|
||||
final String formattedRecord = genericData.toString(record);
|
||||
sb.append(formattedRecord);
|
||||
sb.append(",");
|
||||
// Do not format more than 10 MB of content.
|
||||
if (sb.length() > 1024 * 1024 * 2) {
|
||||
break;
|
||||
// get the transformed xml
|
||||
formatted = writer.toString();
|
||||
} else if ("application/avro-binary".equals(contentType) || "avro/binary".equals(contentType) || "application/avro+binary".equals(contentType)) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append("[");
|
||||
// Use Avro conversions to display logical type values in human readable way.
|
||||
final GenericData genericData = new GenericData();
|
||||
genericData.addLogicalTypeConversion(new Conversions.DecimalConversion());
|
||||
genericData.addLogicalTypeConversion(new TimeConversions.DateConversion());
|
||||
genericData.addLogicalTypeConversion(new TimeConversions.TimeMicrosConversion());
|
||||
genericData.addLogicalTypeConversion(new TimeConversions.TimeMillisConversion());
|
||||
genericData.addLogicalTypeConversion(new TimeConversions.TimestampMicrosConversion());
|
||||
genericData.addLogicalTypeConversion(new TimeConversions.TimestampMillisConversion());
|
||||
genericData.addLogicalTypeConversion(new TimeConversions.LocalTimestampMicrosConversion());
|
||||
genericData.addLogicalTypeConversion(new TimeConversions.LocalTimestampMillisConversion());
|
||||
final DatumReader<GenericData.Record> datumReader = new GenericDatumReader<>(null, null, genericData);
|
||||
try (final DataFileStream<GenericData.Record> dataFileReader = new DataFileStream<>(content.getContentStream(), datumReader)) {
|
||||
while (dataFileReader.hasNext()) {
|
||||
final GenericData.Record record = dataFileReader.next();
|
||||
final String formattedRecord = genericData.toString(record);
|
||||
sb.append(formattedRecord);
|
||||
sb.append(",");
|
||||
// Do not format more than 10 MB of content.
|
||||
if (sb.length() > 1024 * 1024 * 2) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sb.length() > 1) {
|
||||
sb.deleteCharAt(sb.length() - 1);
|
||||
}
|
||||
sb.append("]");
|
||||
final String json = sb.toString();
|
||||
|
||||
final ObjectMapper mapper = new ObjectMapper();
|
||||
final Object objectJson = mapper.readValue(json, Object.class);
|
||||
formatted = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(objectJson);
|
||||
|
||||
contentType = "application/json";
|
||||
} else if ("text/x-yaml".equals(contentType) || "text/yaml".equals(contentType) || "text/yml".equals(contentType)
|
||||
|| "application/x-yaml".equals(contentType) || "application/x-yml".equals(contentType)
|
||||
|| "application/yaml".equals(contentType) || "application/yml".equals(contentType)) {
|
||||
Yaml yaml = new Yaml();
|
||||
// Parse the YAML file
|
||||
final Object yamlObject = yaml.load(content.getContentStream());
|
||||
DumperOptions options = new DumperOptions();
|
||||
options.setIndent(2);
|
||||
options.setPrettyFlow(true);
|
||||
// Fix below - additional configuration
|
||||
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
||||
Yaml output = new Yaml(options);
|
||||
formatted = output.dump(yamlObject);
|
||||
|
||||
} else {
|
||||
// leave plain text alone when formatting
|
||||
formatted = content.getContent();
|
||||
}
|
||||
|
||||
if (sb.length() > 1) {
|
||||
sb.deleteCharAt(sb.length() - 1);
|
||||
}
|
||||
sb.append("]");
|
||||
final String json = sb.toString();
|
||||
|
||||
final ObjectMapper mapper = new ObjectMapper();
|
||||
final Object objectJson = mapper.readValue(json, Object.class);
|
||||
formatted = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(objectJson);
|
||||
|
||||
contentType = "application/json";
|
||||
} else if ("text/x-yaml".equals(contentType) || "text/yaml".equals(contentType) || "text/yml".equals(contentType)
|
||||
|| "application/x-yaml".equals(contentType) || "application/x-yml".equals(contentType)
|
||||
|| "application/yaml".equals(contentType) || "application/yml".equals(contentType)) {
|
||||
Yaml yaml = new Yaml();
|
||||
// Parse the YAML file
|
||||
final Object yamlObject = yaml.load(content.getContentStream());
|
||||
DumperOptions options = new DumperOptions();
|
||||
options.setIndent(2);
|
||||
options.setPrettyFlow(true);
|
||||
// Fix below - additional configuration
|
||||
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
||||
Yaml output = new Yaml(options);
|
||||
formatted = output.dump(yamlObject);
|
||||
|
||||
} else {
|
||||
// leave plain text alone when formatting
|
||||
formatted = content.getContent();
|
||||
} catch (final Throwable t) {
|
||||
logger.warn("Unable to format FlowFile content", t);
|
||||
this.getServletContext().getRequestDispatcher("/WEB-INF/jsp/format-error.jsp").include(request, response);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,22 @@
|
||||
<%--
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
--%>
|
||||
<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
|
||||
<link rel="stylesheet" href="../${project.build.finalName}/css/main.css" type="text/css" />
|
||||
|
||||
<div id="format-error">
|
||||
Unable to format content. Please ensure the content type is correct (defined by the 'mime.type' attribute of the FlowFile).
|
||||
</div>
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#format-error {
|
||||
position: absolute;
|
||||
right: 50px;
|
||||
bottom: 50px;
|
||||
left: 100px;
|
||||
top: 104px;
|
||||
border: 1px solid #aaa;
|
||||
overflow: auto;
|
||||
background-color: #fff;
|
||||
font-style: italic;
|
||||
padding: 5px;
|
||||
font-size: 13px;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user