use of org.opendaylight.restconf.common.context.InstanceIdentifierContext in project netconf by opendaylight.
the class RestconfDocumentedExceptionMapper method toXMLResponseBody.
private static Object toXMLResponseBody(final NormalizedNodeContext errorsNode, final DataNodeContainer errorsSchemaNode) {
final InstanceIdentifierContext<?> pathContext = errorsNode.getInstanceIdentifierContext();
final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
final XMLStreamWriter xmlWriter;
try {
xmlWriter = XML_FACTORY.createXMLStreamWriter(outStream, StandardCharsets.UTF_8.name());
} catch (final XMLStreamException | FactoryConfigurationError e) {
throw new IllegalStateException(e);
}
NormalizedNode data = errorsNode.getData();
SchemaPath schemaPath;
boolean isDataRoot = false;
if (pathContext.getSchemaNode() instanceof SchemaContext) {
isDataRoot = true;
schemaPath = SchemaPath.ROOT;
} else {
final List<QName> qNames = pathContext.getInstanceIdentifier().getPathArguments().stream().filter(arg -> !(arg instanceof NodeIdentifierWithPredicates)).filter(arg -> !(arg instanceof AugmentationIdentifier)).map(PathArgument::getNodeType).collect(Collectors.toList());
schemaPath = SchemaPath.of(Absolute.of(qNames)).getParent();
}
final NormalizedNodeStreamWriter xmlStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(xmlWriter, pathContext.getSchemaContext(), schemaPath);
// We create a delegating writer to special-case error-info as error-info is defined as an empty
// container in the restconf yang schema but we create a leaf node so we can output it. The delegate
// stream writer validates the node type against the schema and thus will expect a LeafSchemaNode but
// the schema has a ContainerSchemaNode so, to avoid an error, we override the leafNode behavior
// for error-info.
final NormalizedNodeStreamWriter streamWriter = new ForwardingNormalizedNodeStreamWriter() {
private boolean inOurLeaf;
@Override
protected NormalizedNodeStreamWriter delegate() {
return xmlStreamWriter;
}
@Override
public void startLeafNode(final NodeIdentifier name) throws IOException {
if (name.getNodeType().equals(RestConfModule.ERROR_INFO_QNAME)) {
String ns = RestConfModule.ERROR_INFO_QNAME.getNamespace().toString();
try {
xmlWriter.writeStartElement(XMLConstants.DEFAULT_NS_PREFIX, RestConfModule.ERROR_INFO_QNAME.getLocalName(), ns);
} catch (XMLStreamException e) {
throw new IOException("Error writing error-info", e);
}
inOurLeaf = true;
} else {
super.startLeafNode(name);
}
}
@Override
public void scalarValue(final Object value) throws IOException {
if (inOurLeaf) {
try {
xmlWriter.writeCharacters(value.toString());
} catch (XMLStreamException e) {
throw new IOException("Error writing error-info", e);
}
} else {
super.scalarValue(value);
}
}
@Override
public void endNode() throws IOException {
if (inOurLeaf) {
try {
xmlWriter.writeEndElement();
} catch (XMLStreamException e) {
throw new IOException("Error writing error-info", e);
}
inOurLeaf = false;
} else {
super.endNode();
}
}
};
final NormalizedNodeWriter nnWriter = NormalizedNodeWriter.forStreamWriter(streamWriter);
try {
if (isDataRoot) {
writeRootElement(xmlWriter, nnWriter, (ContainerNode) data);
} else {
if (data instanceof MapEntryNode) {
// Restconf allows returning one list item. We need to wrap it
// in map node in order to serialize it properly
data = ImmutableNodes.mapNodeBuilder(data.getIdentifier().getNodeType()).addChild((MapEntryNode) data).build();
}
nnWriter.write(data);
nnWriter.flush();
}
} catch (final IOException e) {
LOG.warn("Error writing error response body.", e);
}
return outStream.toString(StandardCharsets.UTF_8);
}
use of org.opendaylight.restconf.common.context.InstanceIdentifierContext in project netconf by opendaylight.
the class JsonToPatchBodyReader method readEditDefinition.
/**
* Read one patch edit object from Json input.
* @param edit PatchEdit instance to be filled with read data
* @param in JsonReader reader
* @param path InstanceIdentifierContext path context
* @param codec StringModuleInstanceIdentifierCodec codec
* @throws IOException if operation fails
*/
private void readEditDefinition(@NonNull final PatchEdit edit, @NonNull final JsonReader in, @NonNull final InstanceIdentifierContext<?> path, @NonNull final StringModuleInstanceIdentifierCodec codec) throws IOException {
final StringBuilder value = new StringBuilder();
in.beginObject();
while (in.hasNext()) {
final String editDefinition = in.nextName();
switch(editDefinition) {
case "edit-id":
edit.setId(in.nextString());
break;
case "operation":
edit.setOperation(PatchEditOperation.valueOf(in.nextString().toUpperCase(Locale.ROOT)));
break;
case "target":
// target can be specified completely in request URI
final String target = in.nextString();
final SchemaInferenceStack stack = SchemaInferenceStack.of(path.getSchemaContext());
if (target.equals("/")) {
edit.setTarget(path.getInstanceIdentifier());
edit.setTargetInference(stack.toInference());
} else {
edit.setTarget(codec.deserialize(codec.serialize(path.getInstanceIdentifier()).concat(target)));
edit.getTarget().getPathArguments().stream().filter(arg -> !(arg instanceof YangInstanceIdentifier.NodeIdentifierWithPredicates)).filter(arg -> !(arg instanceof YangInstanceIdentifier.AugmentationIdentifier)).forEach(p -> stack.enterSchemaTree(p.getNodeType()));
stack.exit();
edit.setTargetInference(stack.toInference());
}
break;
case "value":
// save data defined in value node for next (later) processing, because target needs to be read
// always first and there is no ordering in Json input
readValueNode(value, in);
break;
default:
break;
}
}
in.endObject();
// read saved data to normalized node when target schema is already known
edit.setData(readEditData(new JsonReader(new StringReader(value.toString())), edit.getTargetInference(), path));
}
use of org.opendaylight.restconf.common.context.InstanceIdentifierContext in project netconf by opendaylight.
the class NormalizedNodeJsonBodyWriter method writeNormalizedNode.
private static void writeNormalizedNode(final JsonWriter jsonWriter, final InstanceIdentifierContext<SchemaNode> context, NormalizedNode data, @Nullable final Integer depth) throws IOException {
final RestconfNormalizedNodeWriter nnWriter;
if (context.getSchemaNode() instanceof SchemaContext) {
/*
* Creates writer without initialNs and we write children of root data container
* which is not visible in restconf
*/
nnWriter = createNormalizedNodeWriter(context, SchemaPath.ROOT, jsonWriter, depth);
if (data instanceof ContainerNode) {
writeChildren(nnWriter, (ContainerNode) data);
} else if (data instanceof DOMSourceAnyxmlNode) {
try {
writeChildren(nnWriter, (ContainerNode) NetconfUtil.transformDOMSourceToNormalizedNode(context.getSchemaContext(), ((DOMSourceAnyxmlNode) data).body()).getResult());
} catch (XMLStreamException | URISyntaxException | SAXException e) {
throw new IOException("Cannot write anyxml.", e);
}
}
} else if (context.getSchemaNode() instanceof RpcDefinition) {
/*
* RpcDefinition is not supported as initial codec in JSONStreamWriter,
* so we need to emit initial output declaratation..
*/
final RpcDefinition rpc = (RpcDefinition) context.getSchemaNode();
final SchemaPath path = SchemaPath.of(Absolute.of(rpc.getQName(), rpc.getOutput().getQName()));
nnWriter = createNormalizedNodeWriter(context, path, jsonWriter, depth);
jsonWriter.name("output");
jsonWriter.beginObject();
writeChildren(nnWriter, (ContainerNode) data);
jsonWriter.endObject();
} else {
final List<QName> qnames = context.getInstanceIdentifier().getPathArguments().stream().filter(arg -> !(arg instanceof YangInstanceIdentifier.NodeIdentifierWithPredicates)).filter(arg -> !(arg instanceof YangInstanceIdentifier.AugmentationIdentifier)).map(PathArgument::getNodeType).collect(Collectors.toList());
final SchemaPath path = SchemaPath.of(Absolute.of(qnames)).getParent();
if (data instanceof MapEntryNode) {
data = ImmutableNodes.mapNodeBuilder(data.getIdentifier().getNodeType()).withChild((MapEntryNode) data).build();
}
nnWriter = createNormalizedNodeWriter(context, path, jsonWriter, depth);
nnWriter.write(data);
}
nnWriter.flush();
}
use of org.opendaylight.restconf.common.context.InstanceIdentifierContext in project netconf by opendaylight.
the class RestconfDataServiceImpl method invokeAction.
/**
* Invoke Action operation.
*
* @param payload {@link NormalizedNodePayload} - the body of the operation
* @return {@link NormalizedNodePayload} wrapped in {@link Response}
*/
public Response invokeAction(final NormalizedNodePayload payload) {
final InstanceIdentifierContext<?> context = payload.getInstanceIdentifierContext();
final DOMMountPoint mountPoint = context.getMountPoint();
final YangInstanceIdentifier yangIIdContext = context.getInstanceIdentifier();
final NormalizedNode data = payload.getData();
if (yangIIdContext.isEmpty() && !NETCONF_BASE_QNAME.equals(data.getIdentifier().getNodeType())) {
throw new RestconfDocumentedException("Instance identifier need to contain at least one path argument", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
}
final List<QName> qNames = yangIIdContext.getPathArguments().stream().filter(arg -> !(arg instanceof YangInstanceIdentifier.NodeIdentifierWithPredicates)).filter(arg -> !(arg instanceof YangInstanceIdentifier.AugmentationIdentifier)).map(PathArgument::getNodeType).collect(Collectors.toList());
qNames.add(context.getSchemaNode().getQName());
final Absolute schemaPath = Absolute.of(qNames);
final DOMActionResult response;
final EffectiveModelContext schemaContextRef;
if (mountPoint != null) {
response = invokeAction((ContainerNode) data, schemaPath, yangIIdContext, mountPoint);
schemaContextRef = modelContext(mountPoint);
} else {
response = invokeAction((ContainerNode) data, schemaPath, yangIIdContext, actionService);
schemaContextRef = schemaContextHandler.get();
}
final DOMActionResult result = checkActionResponse(response);
ActionDefinition resultNodeSchema = null;
ContainerNode resultData = null;
if (result != null) {
final Optional<ContainerNode> optOutput = result.getOutput();
if (optOutput.isPresent()) {
resultData = optOutput.get();
resultNodeSchema = (ActionDefinition) context.getSchemaNode();
}
}
if (resultData != null && resultData.isEmpty()) {
return Response.status(Status.NO_CONTENT).build();
}
return Response.status(Status.OK).entity(NormalizedNodePayload.ofNullable(new InstanceIdentifierContext<>(yangIIdContext, resultNodeSchema, mountPoint, schemaContextRef), resultData)).build();
}
use of org.opendaylight.restconf.common.context.InstanceIdentifierContext in project netconf by opendaylight.
the class RestconfDataServiceImplTest method testPutDataWithMountPoint.
@Test
public void testPutDataWithMountPoint() {
final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(iidBase, schemaNode, mountPoint, contextRef);
final NormalizedNodePayload payload = NormalizedNodePayload.of(iidContext, buildBaseCont);
doReturn(immediateTrueFluentFuture()).when(read).exists(LogicalDatastoreType.CONFIGURATION, iidBase);
doNothing().when(readWrite).put(LogicalDatastoreType.CONFIGURATION, iidBase, payload.getData());
final Response response = dataService.putData(null, payload, uriInfo);
assertNotNull(response);
assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus());
}
Aggregations