[OLINGO-1180] Validation of metadata post deserialization

This commit is contained in:
ramya vasanth 2017-10-12 10:46:46 +05:30
parent 379a3c706e
commit 48263a8ce3
11 changed files with 2813 additions and 0 deletions

View File

@ -28,6 +28,7 @@ import org.apache.olingo.client.api.communication.request.retrieve.RetrieveReque
import org.apache.olingo.client.api.domain.ClientObjectFactory;
import org.apache.olingo.client.api.serialization.ClientODataDeserializer;
import org.apache.olingo.client.api.serialization.ODataBinder;
import org.apache.olingo.client.api.serialization.ODataMetadataValidation;
import org.apache.olingo.client.api.serialization.ODataReader;
import org.apache.olingo.client.api.serialization.ODataSerializer;
import org.apache.olingo.client.api.serialization.ODataWriter;
@ -75,4 +76,6 @@ public interface ODataClient {
CUDRequestFactory getCUDRequestFactory();
BatchRequestFactory getBatchRequestFactory();
ODataMetadataValidation metadataValidation();
}

View File

@ -0,0 +1,37 @@
/*
* 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.
*/
package org.apache.olingo.client.api.serialization;
import org.apache.olingo.client.api.edm.xml.XMLMetadata;
import org.apache.olingo.commons.api.edm.Edm;
public interface ODataMetadataValidation {
/**
* This method validates the metadata based on the Edm provided
* @param edm
*/
void validateMetadata(Edm edm);
/**
* This method validates the metadata based on the XMLMetadata provided
* @param xmlMetadata
*/
void validateMetadata(XMLMetadata xmlMetadata);
}

View File

@ -30,6 +30,7 @@ import org.apache.olingo.client.api.communication.request.retrieve.RetrieveReque
import org.apache.olingo.client.api.domain.ClientObjectFactory;
import org.apache.olingo.client.api.serialization.ClientODataDeserializer;
import org.apache.olingo.client.api.serialization.ODataBinder;
import org.apache.olingo.client.api.serialization.ODataMetadataValidation;
import org.apache.olingo.client.api.serialization.ODataReader;
import org.apache.olingo.client.api.serialization.ODataSerializer;
import org.apache.olingo.client.api.serialization.ODataWriter;
@ -47,6 +48,7 @@ import org.apache.olingo.client.core.serialization.AtomSerializer;
import org.apache.olingo.client.core.serialization.ClientODataDeserializerImpl;
import org.apache.olingo.client.core.serialization.JsonSerializer;
import org.apache.olingo.client.core.serialization.ODataBinderImpl;
import org.apache.olingo.client.core.serialization.ODataMetadataValidationImpl;
import org.apache.olingo.client.core.serialization.ODataReaderImpl;
import org.apache.olingo.client.core.serialization.ODataWriterImpl;
import org.apache.olingo.client.core.uri.FilterFactoryImpl;
@ -80,6 +82,8 @@ public class ODataClientImpl implements ODataClient {
protected final Configuration configuration = new ConfigurationImpl();
private final ODataWriter writer = new ODataWriterImpl(this);
private final ODataMetadataValidation metadataValidation = new ODataMetadataValidationImpl();
@Override
public Configuration getConfiguration() {
@ -176,4 +180,9 @@ public class ODataClientImpl implements ODataClient {
public BatchRequestFactory getBatchRequestFactory() {
return batchReqFact;
}
@Override
public ODataMetadataValidation metadataValidation() {
return metadataValidation;
}
}

View File

@ -0,0 +1,491 @@
/*
* 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.
*/
package org.apache.olingo.client.core.metadatavalidator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.olingo.commons.api.edm.FullQualifiedName;
import org.apache.olingo.commons.api.edm.provider.CsdlAction;
import org.apache.olingo.commons.api.edm.provider.CsdlActionImport;
import org.apache.olingo.commons.api.edm.provider.CsdlComplexType;
import org.apache.olingo.commons.api.edm.provider.CsdlEntityContainer;
import org.apache.olingo.commons.api.edm.provider.CsdlEntitySet;
import org.apache.olingo.commons.api.edm.provider.CsdlEntityType;
import org.apache.olingo.commons.api.edm.provider.CsdlFunction;
import org.apache.olingo.commons.api.edm.provider.CsdlFunctionImport;
import org.apache.olingo.commons.api.edm.provider.CsdlNavigationProperty;
import org.apache.olingo.commons.api.edm.provider.CsdlNavigationPropertyBinding;
import org.apache.olingo.commons.api.edm.provider.CsdlStructuralType;
public class CsdlTypeValidator {
private Map<String, String> aliasNamespaceMap = new HashMap<String, String>();
private Map<FullQualifiedName, CsdlEntityContainer> csdlContainersMap =
new HashMap<FullQualifiedName, CsdlEntityContainer>();
private Map<FullQualifiedName, CsdlEntityType> csdlEntityTypesMap =
new HashMap<FullQualifiedName, CsdlEntityType>();
private Map<FullQualifiedName, CsdlComplexType> csdlComplexTypesMap =
new HashMap<FullQualifiedName, CsdlComplexType>();
private Map<FullQualifiedName, CsdlAction> csdlActionsMap =
new HashMap<FullQualifiedName, CsdlAction>();
private Map<FullQualifiedName, CsdlFunction> csdlFunctionsMap =
new HashMap<FullQualifiedName, CsdlFunction>();
/**
*
* @param aliasNamespaceMap
* @param csdlContainersMap
* @param csdlEntityTypesMap
* @param csdlComplexTypesMap
* @param csdlActionsMap
* @param csdlFunctionsMap
* @param csdlTermsMap
*/
public CsdlTypeValidator(Map<String, String> aliasNamespaceMap,
Map<FullQualifiedName, CsdlEntityContainer> csdlContainersMap,
Map<FullQualifiedName, CsdlEntityType> csdlEntityTypesMap,
Map<FullQualifiedName, CsdlComplexType> csdlComplexTypesMap,
Map<FullQualifiedName, CsdlAction> csdlActionsMap,
Map<FullQualifiedName, CsdlFunction> csdlFunctionsMap) {
this.aliasNamespaceMap = aliasNamespaceMap;
this.csdlContainersMap = csdlContainersMap;
this.csdlEntityTypesMap = csdlEntityTypesMap;
this.csdlComplexTypesMap = csdlComplexTypesMap;
this.csdlActionsMap = csdlActionsMap;
this.csdlFunctionsMap = csdlFunctionsMap;
}
/**
* Validates metadata
*/
public void validateMetadataXML() {
validateCsdlEntityTypes();
validateCsdlComplexTypes();
validateCsdlEntitySet();
validateCsdlActionImport();
validateCsdlFunctionImport();
}
/**
* This method validates Csdl Entity types.
* Looks for correct namespace aliases and correct base types
*/
private void validateCsdlEntityTypes() {
for (Map.Entry<FullQualifiedName, CsdlEntityType> entityTypes : csdlEntityTypesMap.entrySet()) {
if (entityTypes.getValue() != null && entityTypes.getKey() != null) {
CsdlEntityType entityType = entityTypes.getValue();
if (entityType.getBaseType() != null) {
CsdlEntityType baseEntityType;
FullQualifiedName baseTypeFQName = entityType.getBaseTypeFQN();
if (!csdlEntityTypesMap.containsKey(baseTypeFQName)) {
FullQualifiedName fqName = validateCsdlEntityTypeWithAlias(baseTypeFQName);
baseEntityType = fetchLastCsdlBaseType(fqName);
} else {
baseEntityType = fetchLastCsdlBaseType(baseTypeFQName);
}
if (baseEntityType != null && (baseEntityType.getKey() == null ||
baseEntityType.getKey().isEmpty())) {
throw new RuntimeException("Missing key for EntityType " + baseEntityType.getName());
}
} else if (entityType.getKey() == null || entityType.getKey().isEmpty()) {
throw new RuntimeException("Missing key for EntityType " + entityType.getName());
}
}
}
}
/**
* This fetches the last Base Type entity from a hierarchy of base type derived types
* @param baseTypeFQName
* @return CsdlEntityType
*/
private CsdlEntityType fetchLastCsdlBaseType(FullQualifiedName baseTypeFQName) {
CsdlEntityType baseEntityType = null;
while (baseTypeFQName != null) {
if (!(csdlEntityTypesMap.containsKey(baseTypeFQName))) {
baseTypeFQName = validateCsdlEntityTypeWithAlias(baseTypeFQName);
}
baseEntityType = csdlEntityTypesMap.get(baseTypeFQName);
if (baseEntityType.getKey() != null) {
break;
} else if (baseEntityType != null && baseEntityType.getBaseType() != null) {
baseTypeFQName = baseEntityType.getBaseTypeFQN();
} else if (baseEntityType.getBaseType() == null) {
break;
}
}
return baseEntityType;
}
/**
* This fetches the last Base Type entity from a hierarchy of base type derived types
* @param baseTypeFQName
* @return CsdlNavigationProperty
*/
private CsdlNavigationProperty fetchLastBaseEntityHavingNavigationProperty(
FullQualifiedName baseTypeFQName, String navBindingProperty) {
CsdlEntityType baseEntityType = null;
while (baseTypeFQName != null) {
if (!(csdlEntityTypesMap.containsKey(baseTypeFQName))) {
baseTypeFQName = validateCsdlEntityTypeWithAlias(baseTypeFQName);
}
baseEntityType = csdlEntityTypesMap.get(baseTypeFQName);
if (baseEntityType.getNavigationProperty(navBindingProperty) != null) {
break;
} else if (baseEntityType != null && baseEntityType.getBaseType() != null) {
baseTypeFQName = baseEntityType.getBaseTypeFQN();
} else if (baseEntityType.getBaseType() == null) {
break;
}
}
if (baseEntityType == null) {
throw new RuntimeException("Entity TYpe is null with fully qualified name:" + baseTypeFQName);
}
return baseEntityType.getNavigationProperty(navBindingProperty);
}
/**
* This validates the namespace to alias mapping
* @param fQName
* @return FullQualifiedName
*/
private FullQualifiedName validateCsdlEntityTypeWithAlias(FullQualifiedName fQName) {
String namespace = aliasNamespaceMap.get(fQName.getNamespace());
FullQualifiedName fqName = new FullQualifiedName(namespace, fQName.getName());
if (!csdlEntityTypesMap.containsKey(fqName)) {
throw new RuntimeException("Invalid Entity Type " + fQName);
}
return fqName;
}
/**
* This validates the namespace to alias mapping
* @param fqName
* @return FullQualifiedName
*/
private FullQualifiedName fetchCorrectNamespaceFromAlias(FullQualifiedName fqName) {
if (aliasNamespaceMap.containsKey(fqName.getNamespace())) {
String namespace = aliasNamespaceMap.get(fqName.getNamespace());
fqName = new FullQualifiedName(namespace, fqName.getName());
}
return fqName;
}
/**
* This method validates Csdl Complex types.
* Looks for correct namespace aliases and correct complex base types
*/
private void validateCsdlComplexTypes() {
for (Map.Entry<FullQualifiedName, CsdlComplexType> complexTypes : csdlComplexTypesMap.entrySet()) {
if (complexTypes.getValue() != null && complexTypes.getKey() != null) {
CsdlComplexType complexType = complexTypes.getValue();
if (complexType.getBaseType() != null) {
FullQualifiedName baseTypeFQName = complexType.getBaseTypeFQN();
if (!csdlComplexTypesMap.containsKey(baseTypeFQName)) {
validateCsdlComplexTypeWithAlias(baseTypeFQName);
}
}
}
}
}
/**
* This validates the namespace to alias mapping
* @param aliasName
* @return
*/
private FullQualifiedName validateCsdlComplexTypeWithAlias(FullQualifiedName aliasName) {
String namespace = aliasNamespaceMap.get(aliasName.getNamespace());
FullQualifiedName fqName = new FullQualifiedName(namespace, aliasName.getName());
if (!csdlComplexTypesMap.containsKey(fqName)) {
throw new RuntimeException("Invalid Complex BaseType " + aliasName);
}
return fqName;
}
/**
* This method validates Csdl entity sets.
* It checks if entity sets are part of correct container and
* entity types defined for entity sets are correct.
*/
private void validateCsdlEntitySet() {
for (Map.Entry<FullQualifiedName, CsdlEntityContainer> container : csdlContainersMap.entrySet()) {
for (CsdlEntitySet entitySet : container.getValue().getEntitySets()) {
FullQualifiedName entityType = entitySet.getTypeFQN();
if (!(csdlEntityTypesMap.containsKey(entityType))) {
validateCsdlEntityTypeWithAlias(entityType);
}
validateNavigationBindingPaths(entitySet, container);
}
}
}
/**
* This method checks if the target entity of the navigation binding path is defined.
* It checks if the type of navigation property of the source entity and target entity is the same
* @param container
* @param CsdlEntitySet
*/
private void validateNavigationBindingPaths(CsdlEntitySet entitySet,
Entry<FullQualifiedName, CsdlEntityContainer> container) {
List<CsdlNavigationPropertyBinding> navigationPropertyBindings = entitySet.getNavigationPropertyBindings();
if (!navigationPropertyBindings.isEmpty()) {
for (CsdlNavigationPropertyBinding navigationPropertyBinding : navigationPropertyBindings) {
String navBindingPath = navigationPropertyBinding.getPath();
String navBindingTarget = navigationPropertyBinding.getTarget();
CsdlEntityType sourceEntityType = null;
if (!(csdlEntityTypesMap.containsKey(new FullQualifiedName(entitySet.getType())))) {
sourceEntityType = csdlEntityTypesMap.get(
validateCsdlEntityTypeWithAlias(new FullQualifiedName(entitySet.getType())));
} else {
sourceEntityType = csdlEntityTypesMap.get(new FullQualifiedName(entitySet.getType()));
}
CsdlNavigationProperty navProperty = null;
String targetType = null;
if (navBindingPath.contains("/")) {
navProperty = findLastQualifiedNameHavingNavigationProperty(navBindingPath, sourceEntityType);
} else {
navProperty = (CsdlNavigationProperty) sourceEntityType.
getNavigationProperty(navBindingPath);
if (navProperty == null) {
navProperty = fetchLastBaseEntityHavingNavigationProperty(
sourceEntityType.getBaseTypeFQN(), navBindingPath);
}
}
if (navBindingTarget.contains("/")) {
targetType = findLastQualifiedTargetName(navBindingTarget);
} else {
if (container.getValue().getEntitySet(navBindingTarget) == null) {
if (container.getValue().getSingleton(navBindingTarget) != null) {
throw new RuntimeException("Validations of Singletons are not supported: "+ navBindingTarget);
} else {
throw new RuntimeException("Navigation Property Target " + navBindingTarget +
" is not part of the same container " + container.getKey());
}
}
FullQualifiedName fqName = container.getValue().getEntitySet(navBindingTarget).getTypeFQN();
if (!(csdlEntityTypesMap.containsKey(fqName))) {
fqName = validateCsdlEntityTypeWithAlias(fqName);
}
targetType = fqName.getFullQualifiedNameAsString();
}
FullQualifiedName navFQName = fetchCorrectNamespaceFromAlias(navProperty.getTypeFQN());
validateReferentialConstraint(sourceEntityType,
csdlEntityTypesMap.get(new FullQualifiedName(targetType)), navProperty);
if (!(navFQName.getFullQualifiedNameAsString().equals(targetType))
&& !(csdlEntityTypesMap.get(navFQName).getBaseTypeFQN() != null &&
fetchCorrectNamespaceFromAlias(csdlEntityTypesMap.get(navFQName).
getBaseTypeFQN()).getFullQualifiedNameAsString().equals(targetType))) {
throw new RuntimeException("Navigation Property Type " +
navFQName +" does not match "
+ "the binding target type " + targetType);
}
}
}
}
/**
* @param sourceEntityType
* @param targetEntityType
* @param navProperty
*/
private void validateReferentialConstraint(CsdlEntityType sourceEntityType, CsdlEntityType targetEntityType,
CsdlNavigationProperty navProperty) {
if (!navProperty.getReferentialConstraints().isEmpty()) {
String propertyName = navProperty.getReferentialConstraints().get(0).getProperty();
if (sourceEntityType.getProperty(propertyName) == null) {
throw new RuntimeException("Property name " + propertyName + " not part of the source entity.");
}
String referencedPropertyName = navProperty.getReferentialConstraints().get(0).getReferencedProperty();
if (targetEntityType.getProperty(referencedPropertyName) == null) {
throw new RuntimeException("Property name " + referencedPropertyName + " not part of the target entity.");
}
}
}
/**
* This looks for the correct entity set
* when the target entity set is part of some other namespace
* e.g <NavigationPropertyBinding Path="Products" Target="SomeModel.SomeContainer/SomeSet" />
* @param navBindingTarget
* @return String
*/
private String findLastQualifiedTargetName(String navBindingTarget) {
String[] targetPaths = navBindingTarget.split("/");
CsdlEntityContainer csdlContainer = csdlContainersMap.containsKey(new FullQualifiedName(targetPaths[0])) ?
csdlContainersMap.get(new FullQualifiedName(targetPaths[0])) :
csdlContainersMap.get(fetchCorrectNamespaceFromAlias(new FullQualifiedName(targetPaths[0])));
if (csdlContainer == null) {
throw new RuntimeException("Container with FullyQualifiedName " + targetPaths[0] + " not found.");
}
String targetEntitySetName = targetPaths[1];
CsdlEntitySet csdlEntitySet = csdlContainer.getEntitySet(targetEntitySetName);
if (csdlEntitySet == null) {
throw new RuntimeException("Target Entity Set mentioned in navigationBindingProperty "
+ "not found in the container " + csdlContainer.getName());
}
FullQualifiedName fqName = csdlEntitySet.getTypeFQN();
if (!(csdlEntityTypesMap.containsKey(fqName))) {
fqName = validateCsdlEntityTypeWithAlias(fqName);
}
return fqName.getFullQualifiedNameAsString();
}
/**
* This looks for the last fully qualified identifier to fetch the navigation property
* e.g if navigation property path is Microsoft.Exchange.Services.OData.Model.ItemAttachment/Item
* then it fetches the entity ItemAttachment and fetches the navigation property Item
* if navigation property path is EntityType/ComplexType/OData.Model.DerivedComplexType/Item
* then it fetches the complex type DerivedComplexType and fetches the navigation property Item
* @param navBindingPath
* @return CsdlNavigationProperty
*/
private CsdlNavigationProperty findLastQualifiedNameHavingNavigationProperty(String navBindingPath,
CsdlEntityType sourceEntityType) {
String[] paths = navBindingPath.split("/");
String lastFullQualifiedName = "";
for (String path : paths) {
if (path.contains(".")) {
lastFullQualifiedName = path;
}
}
String strNavProperty = paths[paths.length - 1];
String remainingPath = navBindingPath.substring(navBindingPath.indexOf(lastFullQualifiedName)
+ lastFullQualifiedName.length() + (lastFullQualifiedName.length() == 0 ? 0 : 1),
navBindingPath.lastIndexOf(strNavProperty));
if (remainingPath.length() > 0) {
remainingPath = remainingPath.substring(0, remainingPath.length() - 1);
}
CsdlNavigationProperty navProperty = null;
CsdlEntityType sourceEntityTypeHavingNavProp = lastFullQualifiedName.length() == 0 ? sourceEntityType :
(csdlEntityTypesMap.containsKey(new FullQualifiedName(lastFullQualifiedName)) ?
csdlEntityTypesMap.get(new FullQualifiedName(lastFullQualifiedName)) :
csdlEntityTypesMap.get(fetchCorrectNamespaceFromAlias(new FullQualifiedName(lastFullQualifiedName))));
if (sourceEntityTypeHavingNavProp == null) {
CsdlComplexType sourceComplexTypeHavingNavProp =
csdlComplexTypesMap.containsKey(new FullQualifiedName(lastFullQualifiedName)) ?
csdlComplexTypesMap.get(new FullQualifiedName(lastFullQualifiedName)) :
csdlComplexTypesMap.get(fetchCorrectNamespaceFromAlias(new FullQualifiedName(lastFullQualifiedName)));
if (sourceComplexTypeHavingNavProp == null) {
throw new RuntimeException("The fully Qualified type " + lastFullQualifiedName +
" mentioned in navigation binding path not found ");
}
navProperty = remainingPath.length() > 0 ? fetchNavigationProperty(remainingPath, strNavProperty,
sourceComplexTypeHavingNavProp) : sourceComplexTypeHavingNavProp.getNavigationProperty(strNavProperty);
} else {
navProperty = remainingPath.length() > 0 ? fetchNavigationProperty(remainingPath, strNavProperty,
sourceEntityTypeHavingNavProp) : sourceEntityTypeHavingNavProp.getNavigationProperty(strNavProperty);
}
return navProperty;
}
/**
* fetch the actual navigation property from the remaning path
* @param remainingPath
* @param strNavProperty
* @param sourceTypeHavingNavProp
* @return CsdlNavigationProperty
*/
private CsdlNavigationProperty fetchNavigationProperty(String remainingPath,
String strNavProperty, CsdlStructuralType sourceTypeHavingNavProp) {
String[] paths = remainingPath.split("/");
for (String path : paths) {
FullQualifiedName fqName = null;
if (sourceTypeHavingNavProp instanceof CsdlComplexType) {
fqName = ((CsdlComplexType)sourceTypeHavingNavProp).getProperty(path).getTypeAsFQNObject();
} else if (sourceTypeHavingNavProp instanceof CsdlEntityType) {
fqName = ((CsdlEntityType)sourceTypeHavingNavProp).getProperty(path).getTypeAsFQNObject();
}
if (fqName != null) {
String namespace = aliasNamespaceMap.get(fqName.getNamespace());
fqName = namespace != null ? new FullQualifiedName(namespace, fqName.getName()) : fqName;
}
sourceTypeHavingNavProp = csdlEntityTypesMap.get(fqName) != null ?
csdlEntityTypesMap.get(fqName) :
csdlComplexTypesMap.get(fqName);
}
return sourceTypeHavingNavProp.getNavigationProperty(strNavProperty);
}
/**
* This method validates Csdl action import.
* It checks if action imports are part of correct container and
* actions defined for action imports are correct
*/
private void validateCsdlActionImport() {
for (Map.Entry<FullQualifiedName, CsdlEntityContainer> container : csdlContainersMap.entrySet()) {
for (CsdlActionImport actionImport : container.getValue().getActionImports()) {
FullQualifiedName fqaction = actionImport.getActionFQN();
if (!(csdlActionsMap.containsKey(fqaction))) {
validateCsdlActionsWithAlias(fqaction);
}
}
}
}
/**
* This validates the namespace to alias mapping
* @param aliasName
* @return FullQualifiedName
*/
private FullQualifiedName validateCsdlActionsWithAlias(FullQualifiedName aliasName) {
String namespace = aliasNamespaceMap.get(aliasName.getNamespace());
FullQualifiedName fqName = new FullQualifiedName(namespace, aliasName.getName());
if (!csdlActionsMap.containsKey(fqName)) {
throw new RuntimeException("Invalid Action " + aliasName);
}
return fqName;
}
/**
* This methods validates csdl function imports.
* It checks if function imports are part of correct container and
* functions defined for function imports are correct
*/
private void validateCsdlFunctionImport() {
for (Map.Entry<FullQualifiedName, CsdlEntityContainer> container : csdlContainersMap.entrySet()) {
for (CsdlFunctionImport functionImport : container.getValue().getFunctionImports()) {
FullQualifiedName fqaction = functionImport.getFunctionFQN();
if (!(csdlFunctionsMap.containsKey(fqaction))) {
validateCsdlFunctionsWithAlias(fqaction);
}
}
}
}
/**
* This validates the namespace to alias mapping
* @param aliasName
* @return FullQualifiedName
*/
private FullQualifiedName validateCsdlFunctionsWithAlias(FullQualifiedName aliasName) {
String namespace = aliasNamespaceMap.get(aliasName.getNamespace());
FullQualifiedName fqName = new FullQualifiedName(namespace, aliasName.getName());
if (!csdlFunctionsMap.containsKey(fqName)) {
throw new RuntimeException("Invalid Function " + aliasName);
}
return fqName;
}
}

View File

@ -0,0 +1,294 @@
/*
* 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.
*/
package org.apache.olingo.client.core.metadatavalidator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.olingo.commons.api.edm.EdmBindingTarget;
import org.apache.olingo.commons.api.edm.EdmComplexType;
import org.apache.olingo.commons.api.edm.EdmEntityContainer;
import org.apache.olingo.commons.api.edm.EdmEntitySet;
import org.apache.olingo.commons.api.edm.EdmEntityType;
import org.apache.olingo.commons.api.edm.EdmFunction;
import org.apache.olingo.commons.api.edm.EdmFunctionImport;
import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
import org.apache.olingo.commons.api.edm.EdmNavigationPropertyBinding;
import org.apache.olingo.commons.api.edm.EdmSingleton;
import org.apache.olingo.commons.api.edm.EdmStructuredType;
import org.apache.olingo.commons.api.edm.FullQualifiedName;
public class EdmTypeValidator {
private Map<String, String> aliasNamespaceMap = new HashMap<String, String>();
private Map<FullQualifiedName, EdmEntityContainer> edmContainersMap =
new HashMap<FullQualifiedName, EdmEntityContainer>();
private Map<FullQualifiedName, EdmEntityType> edmEntityTypesMap =
new HashMap<FullQualifiedName, EdmEntityType>();
private Map<FullQualifiedName, EdmComplexType> edmComplexTypesMap =
new HashMap<FullQualifiedName, EdmComplexType>();
private Map<FullQualifiedName, EdmFunction> edmFunctionsMap =
new HashMap<FullQualifiedName, EdmFunction>();
/**
*
* @param aliasNamespaceMap
* @param edmContainersMap
* @param edmEntityTypesMap
* @param edmComplexTypesMap
* @param edmFunctionsMap
* @param edmTermsMap
*/
public EdmTypeValidator(Map<String, String> aliasNamespaceMap,
Map<FullQualifiedName, EdmEntityContainer> edmContainersMap,
Map<FullQualifiedName, EdmEntityType> edmEntityTypesMap,
Map<FullQualifiedName, EdmComplexType> edmComplexTypesMap,
Map<FullQualifiedName, EdmFunction> edmFunctionsMap) {
this.aliasNamespaceMap = aliasNamespaceMap;
this.edmContainersMap = edmContainersMap;
this.edmEntityTypesMap = edmEntityTypesMap;
this.edmComplexTypesMap = edmComplexTypesMap;
this.edmFunctionsMap = edmFunctionsMap;
}
/**
* Validates Edm
*/
public void validateEdm() {
validateEdmEntityTypes();
validateEdmEntitySet();
validateEdmFunctionImport();
}
/**
* This method validates Edm Entity types.
* Looks for correct namespace aliases and correct base types
*/
private void validateEdmEntityTypes() {
for (Map.Entry<FullQualifiedName, EdmEntityType> entityTypes : edmEntityTypesMap.entrySet()) {
if (entityTypes.getValue() != null && entityTypes.getKey() != null) {
EdmEntityType entityType = entityTypes.getValue();
if (entityType.getBaseType() != null) {
FullQualifiedName baseTypeFQName = entityType.getBaseType().getFullQualifiedName();
EdmEntityType baseEntityType = edmEntityTypesMap.get(baseTypeFQName);
if (baseEntityType != null && baseEntityType.getKeyPredicateNames().isEmpty()) {
throw new RuntimeException("Missing key for EntityType " + baseEntityType.getName());
}
} else if (entityType.getKeyPredicateNames().isEmpty()) {
throw new RuntimeException("Missing key for EntityType " + entityType.getName());
}
}
}
}
/**
* This method validates Edm entity sets.
* It checks if entity sets are part of correct container and
* entity types defined for entity sets are correct and
* validates navigation property bindings
*/
private void validateEdmEntitySet() {
for (Map.Entry<FullQualifiedName, EdmEntityContainer> container : edmContainersMap.entrySet()) {
for (EdmEntitySet entitySet : container.getValue().getEntitySets()) {
validateNavigationBindingPaths(entitySet);
}
}
}
/**
* This method checks if the target entity of the navigation binding path is defined.
* It checks if the type of navigation property of the source entity and target entity is the same
* @param entitySet
* @param container
*/
private void validateNavigationBindingPaths(EdmEntitySet entitySet) {
List<EdmNavigationPropertyBinding> navigationPropertyBindings = entitySet.getNavigationPropertyBindings();
if (!navigationPropertyBindings.isEmpty()) {
for (EdmNavigationPropertyBinding navigationPropertyBinding : navigationPropertyBindings) {
String navBindingPath = navigationPropertyBinding.getPath();
EdmBindingTarget edmBindingTarget = entitySet.getRelatedBindingTarget(navBindingPath);
EdmEntityType sourceEntityType = edmEntityTypesMap.get(entitySet.getEntityType().getFullQualifiedName());
if (edmBindingTarget instanceof EdmSingleton) {
throw new RuntimeException("Validations of Singletons are not supported: " + edmBindingTarget.getName());
}
EdmEntityType targetEntityType = edmBindingTarget.getEntityType();
EdmNavigationProperty navProperty = null;
if (navBindingPath.contains("/")) {
navProperty = findLastQualifiedNameHavingNavigationProperty(navBindingPath, sourceEntityType);
} else {
navProperty = (EdmNavigationProperty) sourceEntityType.getProperty(navBindingPath);
}
FullQualifiedName navFQName = fetchCorrectNamespaceFromAlias(navProperty.getType().getFullQualifiedName());
validateReferentialConstraint(sourceEntityType, targetEntityType, navProperty);
String targetType = targetEntityType.getFullQualifiedName().getFullQualifiedNameAsString();
if (!(navFQName.getFullQualifiedNameAsString().equals(targetType))
&& !(navProperty.getType().compatibleTo(targetEntityType))) {
throw new RuntimeException("Navigation Property Type " +
navProperty.getType().getFullQualifiedName() +" does not match "
+ "the binding target type " + targetType);
}
}
}
}
/**
* @param sourceEntityType
* @param targetEntityType
* @param navProperty
*/
private void validateReferentialConstraint(EdmEntityType sourceEntityType, EdmEntityType targetEntityType,
EdmNavigationProperty navProperty) {
if (!navProperty.getReferentialConstraints().isEmpty()) {
String propertyName = navProperty.getReferentialConstraints().get(0).getPropertyName();
if (sourceEntityType.getProperty(propertyName) == null) {
throw new RuntimeException("Property name "+ propertyName + " not part of the source entity.");
}
String referencedPropertyName = navProperty.getReferentialConstraints().get(0).getReferencedPropertyName();
if (targetEntityType.getProperty(referencedPropertyName) == null) {
throw new RuntimeException("Property name " + referencedPropertyName + " not part of the target entity.");
}
}
}
/**
* This looks for the last fully qualified identifier to fetch the navigation property
* e.g if navigation property path is Microsoft.Exchange.Services.OData.Model.ItemAttachment/Item
* then it fetches the entity ItemAttachment and fetches the navigation property Item
* if navigation property path is EntityType/ComplexType/OData.Model.DerivedComplexType/Item
* then it fetches the complex type DerivedComplexType and fetches the navigation property Item
* @param navBindingPath
* @param sourceEntityType
* @return EdmNavigationProperty
*/
private EdmNavigationProperty findLastQualifiedNameHavingNavigationProperty(String navBindingPath,
EdmEntityType sourceEntityType) {
String[] paths = navBindingPath.split("/");
String lastFullQualifiedName = "";
for (String path : paths) {
if (path.contains(".")) {
lastFullQualifiedName = path;
}
}
String strNavProperty = paths[paths.length - 1];
String remainingPath = navBindingPath.substring(navBindingPath.indexOf(lastFullQualifiedName)
+ lastFullQualifiedName.length() + (lastFullQualifiedName.length() == 0 ? 0 : 1),
navBindingPath.lastIndexOf(strNavProperty));
if (remainingPath.length() > 0) {
remainingPath = remainingPath.substring(0, remainingPath.length() - 1);
}
EdmNavigationProperty navProperty = null;
EdmEntityType sourceEntityTypeHavingNavProp = lastFullQualifiedName.length() == 0 ? sourceEntityType :
(edmEntityTypesMap.containsKey(new FullQualifiedName(lastFullQualifiedName)) ?
edmEntityTypesMap.get(new FullQualifiedName(lastFullQualifiedName)) :
edmEntityTypesMap.get(fetchCorrectNamespaceFromAlias(new FullQualifiedName(lastFullQualifiedName))));
if (sourceEntityTypeHavingNavProp == null) {
EdmComplexType sourceComplexTypeHavingNavProp =
edmComplexTypesMap.containsKey(new FullQualifiedName(lastFullQualifiedName)) ?
edmComplexTypesMap.get(new FullQualifiedName(lastFullQualifiedName)) :
edmComplexTypesMap.get(fetchCorrectNamespaceFromAlias(new FullQualifiedName(lastFullQualifiedName)));
if (sourceComplexTypeHavingNavProp == null) {
throw new RuntimeException("The fully Qualified type " + lastFullQualifiedName +
" mentioned in navigation binding path not found ");
}
navProperty = remainingPath.length() > 0 ? fetchNavigationProperty(remainingPath, strNavProperty,
sourceComplexTypeHavingNavProp) : sourceComplexTypeHavingNavProp.getNavigationProperty(strNavProperty);
} else {
navProperty = remainingPath.length() > 0 ? fetchNavigationProperty(remainingPath, strNavProperty,
sourceEntityTypeHavingNavProp) : sourceEntityTypeHavingNavProp.getNavigationProperty(strNavProperty);
}
return navProperty;
}
/**
* Fetch the correct navigation property from the remaining path
* @param remainingPath
* @param strNavProperty
* @param sourceTypeHavingNavProp
* @return EdmNavigationProperty
*/
private EdmNavigationProperty fetchNavigationProperty(String remainingPath,
String strNavProperty, EdmStructuredType sourceTypeHavingNavProp) {
String[] paths = remainingPath.split("/");
for (String path : paths) {
FullQualifiedName fqName = null;
if (sourceTypeHavingNavProp instanceof EdmComplexType) {
fqName = ((EdmComplexType)sourceTypeHavingNavProp).getProperty(path).getType().getFullQualifiedName();
} else if (sourceTypeHavingNavProp instanceof EdmEntityType) {
fqName = ((EdmEntityType)sourceTypeHavingNavProp).getProperty(path).getType().getFullQualifiedName();
}
if (fqName != null) {
String namespace = aliasNamespaceMap.get(fqName.getNamespace());
fqName = namespace != null ? new FullQualifiedName(namespace, fqName.getName()) : fqName;
}
sourceTypeHavingNavProp = edmEntityTypesMap.containsKey(fqName) ?
edmEntityTypesMap.get(fqName) :
edmComplexTypesMap.get(fqName);
}
return sourceTypeHavingNavProp.getNavigationProperty(strNavProperty);
}
/**
* This validates the namespace to alias mapping
* @param fQName
* @return FullQualifiedName
*/
private FullQualifiedName fetchCorrectNamespaceFromAlias(FullQualifiedName fqName) {
if (aliasNamespaceMap.containsKey(fqName.getNamespace())) {
String namespace = aliasNamespaceMap.get(fqName.getNamespace());
fqName = new FullQualifiedName(namespace, fqName.getName());
}
return fqName;
}
/**
* This methods validates edm function imports.
* It checks if function imports are part of correct container and
* functions defined for function imports are correct
*/
private void validateEdmFunctionImport() {
for (Map.Entry<FullQualifiedName, EdmEntityContainer> container : edmContainersMap.entrySet()) {
for (EdmFunctionImport functionImport : container.getValue().getFunctionImports()) {
FullQualifiedName fqFunction = functionImport.getFunctionFqn();
if (!(edmFunctionsMap.containsKey(fqFunction))) {
validateEdmFunctionsWithAlias(fqFunction);
}
}
}
}
/**
* This validates the namespace to alias mapping
* @param aliasName
* @return FullQualifiedName
*/
private FullQualifiedName validateEdmFunctionsWithAlias(FullQualifiedName aliasName) {
String namespace = aliasNamespaceMap.get(aliasName.getNamespace());
FullQualifiedName fqName = new FullQualifiedName(namespace, aliasName.getName());
if (!edmFunctionsMap.containsKey(fqName)) {
throw new RuntimeException("Invalid Function " + aliasName);
}
return fqName;
}
}

View File

@ -0,0 +1,118 @@
/*
* 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.
*/
package org.apache.olingo.client.core.serialization;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.olingo.client.api.edm.xml.XMLMetadata;
import org.apache.olingo.client.api.serialization.ODataMetadataValidation;
import org.apache.olingo.client.core.metadatavalidator.CsdlTypeValidator;
import org.apache.olingo.client.core.metadatavalidator.EdmTypeValidator;
import org.apache.olingo.commons.api.edm.Edm;
import org.apache.olingo.commons.api.edm.EdmAction;
import org.apache.olingo.commons.api.edm.EdmComplexType;
import org.apache.olingo.commons.api.edm.EdmEntityContainer;
import org.apache.olingo.commons.api.edm.EdmEntityType;
import org.apache.olingo.commons.api.edm.EdmFunction;
import org.apache.olingo.commons.api.edm.EdmSchema;
import org.apache.olingo.commons.api.edm.FullQualifiedName;
import org.apache.olingo.commons.api.edm.provider.CsdlAction;
import org.apache.olingo.commons.api.edm.provider.CsdlComplexType;
import org.apache.olingo.commons.api.edm.provider.CsdlEntityContainer;
import org.apache.olingo.commons.api.edm.provider.CsdlEntityType;
import org.apache.olingo.commons.api.edm.provider.CsdlFunction;
import org.apache.olingo.commons.api.edm.provider.CsdlSchema;
public class ODataMetadataValidationImpl implements ODataMetadataValidation {
@Override
public void validateMetadata(Edm edm) {
Map<FullQualifiedName, EdmEntityType> edmEntityTypesMap = new HashMap<FullQualifiedName, EdmEntityType>();
Map<FullQualifiedName, EdmComplexType> edmComplexTypesMap = new HashMap<FullQualifiedName, EdmComplexType>();
Map<FullQualifiedName, EdmFunction> edmFunctionsMap = new HashMap<FullQualifiedName, EdmFunction>();
Map<FullQualifiedName, EdmEntityContainer> edmContainersMap = new HashMap<FullQualifiedName, EdmEntityContainer>();
Map<String, String> aliasNamespaceMap = new HashMap<String, String>();
List<EdmSchema> edmSchemas = edm.getSchemas();
for (EdmSchema edmSchema : edmSchemas) {
List<EdmEntityType> edmEntityTypes = edmSchema.getEntityTypes();
for (EdmEntityType edmEntityType : edmEntityTypes) {
edmEntityTypesMap.put(edmEntityType.getFullQualifiedName(), edmEntityType);
}
List<EdmComplexType> edmComplexTypes = edmSchema.getComplexTypes();
for (EdmComplexType edmComplexType : edmComplexTypes) {
edmComplexTypesMap.put(edmComplexType.getFullQualifiedName(), edmComplexType);
}
List<EdmFunction> edmFunctions = edmSchema.getFunctions();
for (EdmFunction edmFunction : edmFunctions) {
edmFunctionsMap.put(edmFunction.getFullQualifiedName(), edmFunction);
}
aliasNamespaceMap.put(edmSchema.getAlias(), edmSchema.getNamespace());
if (edmSchema.getEntityContainer() != null) {
edmContainersMap.put(edmSchema.getEntityContainer().getFullQualifiedName(), edmSchema.getEntityContainer());
}
}
EdmTypeValidator edmTypeValidator = new EdmTypeValidator(aliasNamespaceMap, edmContainersMap,
edmEntityTypesMap, edmComplexTypesMap, edmFunctionsMap);
edmTypeValidator.validateEdm();
}
@Override
public void validateMetadata(XMLMetadata xmlMetadata) {
Map<FullQualifiedName, CsdlEntityType> csdlEntityTypesMap = new HashMap<FullQualifiedName, CsdlEntityType>();
Map<FullQualifiedName, CsdlComplexType> csdlComplexTypesMap = new HashMap<FullQualifiedName, CsdlComplexType>();
Map<FullQualifiedName, CsdlAction> csdlActionsMap = new HashMap<FullQualifiedName, CsdlAction>();
Map<FullQualifiedName, CsdlFunction> csdlFunctionsMap = new HashMap<FullQualifiedName, CsdlFunction>();
Map<FullQualifiedName, CsdlEntityContainer> csdlContainersMap =
new HashMap<FullQualifiedName, CsdlEntityContainer>();
Map<String, String> aliasNamespaceMap = new HashMap<String, String>();
List<CsdlSchema> csdlSchemas = xmlMetadata.getSchemas();
for (CsdlSchema csdlSchema : csdlSchemas) {
List<CsdlEntityType> csdlEntityTypes = csdlSchema.getEntityTypes();
for (CsdlEntityType csdlEntityType : csdlEntityTypes) {
csdlEntityTypesMap.put(new FullQualifiedName(
csdlSchema.getNamespace(), csdlEntityType.getName()), csdlEntityType);
}
List<CsdlComplexType> csdlComplexTypes = csdlSchema.getComplexTypes();
for (CsdlComplexType csdlComplexType : csdlComplexTypes) {
csdlComplexTypesMap.put(new FullQualifiedName(
csdlSchema.getNamespace(), csdlComplexType.getName()), csdlComplexType);
}
List<CsdlAction> csdlActions = csdlSchema.getActions();
for (CsdlAction csdlAction : csdlActions) {
csdlActionsMap.put(new FullQualifiedName(
csdlSchema.getNamespace(), csdlAction.getName()), csdlAction);
}
List<CsdlFunction> csdlFunctions = csdlSchema.getFunctions();
for (CsdlFunction csdlFunction : csdlFunctions) {
csdlFunctionsMap.put(
new FullQualifiedName(csdlSchema.getNamespace(), csdlFunction.getName()), csdlFunction);
}
aliasNamespaceMap.put(csdlSchema.getAlias(), csdlSchema.getNamespace());
if (csdlSchema.getEntityContainer() != null) {
csdlContainersMap.put(new FullQualifiedName(
csdlSchema.getNamespace(), csdlSchema.getEntityContainer().getName()), csdlSchema.getEntityContainer());
}
}
CsdlTypeValidator csdlTypeValidator = new CsdlTypeValidator(aliasNamespaceMap, csdlContainersMap,
csdlEntityTypesMap, csdlComplexTypesMap, csdlActionsMap, csdlFunctionsMap);
csdlTypeValidator.validateMetadataXML();
}
}

View File

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
<edmx:DataServices m:DataServiceVersion="4.0" m:MaxDataServiceVersion="4.0" xmlns:m="http://docs.oasis-open.org/odata/ns/metadata">
<Schema Namespace="Microsoft.Exchange.Services.OData.Model" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<EntityType Name="Customer" Abstract="true">
<Key>
<PropertyRef Name="CustomerId" />
</Key>
<Property Name="CustomerId" Type="Edm.String" Nullable="false"/>
<Property Name="CustomerName" Type="Edm.String" />
<Property Name="Pet" Type="Microsoft.Exchange.Services.OData.Model.Animal" />
</EntityType>
<EntityType Name="VipCustomer" BaseType="Microsoft.Exchange.Services.OData.Model.Customer" Abstract="true">
<Property Name="CustomerType" Type="Edm.String" />
<Property Name="VipLocations" Type="Collection(Microsoft.Exchange.Services.OData.Model.Address)" />
</EntityType>
<EntityType Name="City">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="Edm.String" Nullable="false"/>
</EntityType>
<ComplexType Name="Animal">
</ComplexType>
<ComplexType Name="Human" BaseType="Microsoft.Exchange.Services.OData.Model.Animal">
<Property Name="HumanAddress" Type="Microsoft.Exchange.Services.OData.Model.USAddress" />
</ComplexType>
<ComplexType Name="Horse" BaseType="Microsoft.Exchange.Services.OData.Model.Animal">
<Property Name="HorseAddress" Type="Microsoft.Exchange.Services.OData.Model.USAddress" />
<Property Name="HorseAddresses" Type="Collection(Microsoft.Exchange.Services.OData.Model.USAddress)" />
</ComplexType>
<ComplexType Name="Address">
<NavigationProperty Name="City" Type="Microsoft.Exchange.Services.OData.Model.City" />
</ComplexType>
<ComplexType Name="USAddress" BaseType="Microsoft.Exchange.Services.OData.Model.Address">
<Property Name="CountryAddress" Type="Microsoft.Exchange.Services.OData.Model.CountryAddress" />
<NavigationProperty Name="SubCity" Type="Microsoft.Exchange.Services.OData.Model.City" />
</ComplexType>
<ComplexType Name="CountryAddress" BaseType="Microsoft.Exchange.Services.OData.Model.Address">
<NavigationProperty Name="SubCity" Type="Microsoft.Exchange.Services.OData.Model.City" />
</ComplexType>
<EntityContainer Name="EntityContainer" m:IsDefaultEntityContainer="true">
<EntitySet Name="Customers" EntityType="Microsoft.Exchange.Services.OData.Model.Customer">
<NavigationPropertyBinding Path="Pet/Microsoft.Exchange.Services.OData.Model.Human/HumanAddress/SubCity" Target="HumanCities" />
<NavigationPropertyBinding Path="Pet/Microsoft.Exchange.Services.OData.Model.Horse/HorseAddress/SubCity" Target="HorseCities" />
<NavigationPropertyBinding Path="Pet/Microsoft.Exchange.Services.OData.Model.Horse/HorseAddresses/SubCity" Target="HorseCities" />
<NavigationPropertyBinding Path="Microsoft.Exchange.Services.OData.Model.VipCustomer/VipLocations/Microsoft.Exchange.Services.OData.Model.USAddress/SubCity" Target="HumanCities" />
<NavigationPropertyBinding Path="Microsoft.Exchange.Services.OData.Model.VipCustomer/VipLocations/Microsoft.Exchange.Services.OData.Model.USAddress/CountryAddress/SubCity" Target="HumanCities" />
<NavigationPropertyBinding Path="Microsoft.Exchange.Services.OData.Model.VipCustomer/VipLocations/City" Target="HumanCities" />
</EntitySet>
<EntitySet Name="HumanCities" EntityType="Microsoft.Exchange.Services.OData.Model.City"/>
<EntitySet Name="HorseCities" EntityType="Microsoft.Exchange.Services.OData.Model.City"/>
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>

View File

@ -0,0 +1,88 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
<edmx:DataServices m:DataServiceVersion="4.0" m:MaxDataServiceVersion="4.0" xmlns:m="http://docs.oasis-open.org/odata/ns/metadata">
<Schema Namespace="Microsoft.Exchange.Services.OData.Model" xmlns="http://docs.oasis-open.org/odata/ns/edm" Alias="Namespace1_Alias">
<EntityType Name="Customer" Abstract="true">
<Key>
<PropertyRef Name="CustomerId" />
</Key>
<Property Name="CustomerId" Type="Edm.String" Nullable="false"/>
<Property Name="CustomerName" Type="Edm.String" />
<Property Name="Pet" Type="Microsoft.Exchange.Services.OData.Model.Animal" />
</EntityType>
<EntityType Name="VipCustomer" BaseType="Microsoft.Exchange.Services.OData.Model.Customer" Abstract="true">
<Property Name="CustomerType" Type="Edm.String" />
<Property Name="VipLocations" Type="Collection(Microsoft.Exchange.Services.OData.Model.Address)" />
</EntityType>
<EntityType Name="City">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="Edm.String" Nullable="false"/>
</EntityType>
<ComplexType Name="Animal">
</ComplexType>
<ComplexType Name="Human" BaseType="Microsoft.Exchange.Services.OData.Model.Animal">
<Property Name="HumanAddress" Type="Microsoft.Exchange.Services.OData.Model.USAddress" />
</ComplexType>
<ComplexType Name="Horse" BaseType="Microsoft.Exchange.Services.OData.Model.Animal">
<Property Name="HorseAddress" Type="Microsoft.Exchange.Services.OData.Model.USAddress" />
<Property Name="HorseAddresses" Type="Collection(Microsoft.Exchange.Services.OData.Model.USAddress)" />
</ComplexType>
<ComplexType Name="Address">
<NavigationProperty Name="City" Type="Microsoft.Exchange.Services.OData.Model.City" />
</ComplexType>
<ComplexType Name="USAddress" BaseType="Microsoft.Exchange.Services.OData.Model.Address">
<Property Name="CountryAddress" Type="Microsoft.Exchange.Services.OData.Model.CountryAddress" />
<NavigationProperty Name="SubCity" Type="Microsoft.Exchange.Services.OData.Model.City" />
</ComplexType>
<ComplexType Name="CountryAddress" BaseType="Microsoft.Exchange.Services.OData.Model.Address">
<NavigationProperty Name="SubCity" Type="Microsoft.Exchange.Services.OData.Model.City" />
</ComplexType>
<EntityContainer Name="EntityContainer" m:IsDefaultEntityContainer="true">
<EntitySet Name="Customers" EntityType="Microsoft.Exchange.Services.OData.Model.Customer">
<NavigationPropertyBinding Path="Pet/Microsoft.Exchange.Services.OData.Model.Human/HumanAddress/SubCity" Target="ODataWebExperimental.OData.Model.EntityContainer1/HumanCities" />
<NavigationPropertyBinding Path="Pet/Microsoft.Exchange.Services.OData.Model.Horse/HorseAddress/SubCity" Target="HorseCities" />
<NavigationPropertyBinding Path="Pet/Microsoft.Exchange.Services.OData.Model.Horse/HorseAddresses/SubCity" Target="HorseCities" />
<NavigationPropertyBinding Path="Microsoft.Exchange.Services.OData.Model.VipCustomer/VipLocations/Microsoft.Exchange.Services.OData.Model.USAddress/SubCity" Target="ODataWebExperimental.OData.Model.EntityContainer1/HumanCities" />
<NavigationPropertyBinding Path="Microsoft.Exchange.Services.OData.Model.VipCustomer/VipLocations/Microsoft.Exchange.Services.OData.Model.USAddress/CountryAddress/SubCity" Target="ODataWebExperimental.OData.Model.EntityContainer1/HumanCities" />
<NavigationPropertyBinding Path="Microsoft.Exchange.Services.OData.Model.VipCustomer/VipLocations/City" Target="ODataWebExperimental.OData.Model.EntityContainer1/HumanCities" />
</EntitySet>
<EntitySet Name="HorseCities" EntityType="Microsoft.Exchange.Services.OData.Model.City"/>
</EntityContainer>
</Schema>
<Schema Namespace="ODataWebExperimental.OData.Model" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<EntityContainer Name="EntityContainer1" p4:LazyLoadingEnabled="true" xmlns:p4="http://schemas.microsoft.com/ado/2009/02/edm/annotation">
<EntitySet Name="HumanCities" EntityType="Microsoft.Exchange.Services.OData.Model.City"/>
<EntitySet Name="Customers" EntityType="Microsoft.Exchange.Services.OData.Model.Customer">
<NavigationPropertyBinding Path="Pet/Microsoft.Exchange.Services.OData.Model.Human/HumanAddress/SubCity" Target="HumanCities" />
<NavigationPropertyBinding Path="Pet/Microsoft.Exchange.Services.OData.Model.Horse/HorseAddress/SubCity" Target="Namespace1_Alias.EntityContainer/HorseCities" />
<NavigationPropertyBinding Path="Pet/Microsoft.Exchange.Services.OData.Model.Horse/HorseAddresses/SubCity" Target="Microsoft.Exchange.Services.OData.Model.EntityContainer/HorseCities" />
<NavigationPropertyBinding Path="Microsoft.Exchange.Services.OData.Model.VipCustomer/VipLocations/Microsoft.Exchange.Services.OData.Model.USAddress/SubCity" Target="HumanCities" />
<NavigationPropertyBinding Path="Microsoft.Exchange.Services.OData.Model.VipCustomer/VipLocations/Microsoft.Exchange.Services.OData.Model.USAddress/CountryAddress/SubCity" Target="HumanCities" />
<NavigationPropertyBinding Path="Microsoft.Exchange.Services.OData.Model.VipCustomer/VipLocations/City" Target="HumanCities" />
</EntitySet>
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>

View File

@ -0,0 +1,88 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
<edmx:DataServices m:DataServiceVersion="4.0" m:MaxDataServiceVersion="4.0" xmlns:m="http://docs.oasis-open.org/odata/ns/metadata">
<Schema Namespace="Microsoft.Exchange.Services.OData.Model" Alias="Namespace1_Alias" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<EntityType Name="Customer" Abstract="true">
<Key>
<PropertyRef Name="CustomerId" />
</Key>
<Property Name="CustomerId" Type="Edm.String" Nullable="false"/>
<Property Name="CustomerName" Type="Edm.String" />
<Property Name="Pet" Type="Microsoft.Exchange.Services.OData.Model.Animal" />
</EntityType>
<EntityType Name="VipCustomer" BaseType="Namespace1_Alias.Customer" Abstract="true">
<Property Name="CustomerType" Type="Edm.String" />
<Property Name="VipLocations" Type="Collection(Namespace1_Alias.Address)" />
</EntityType>
<EntityType Name="City">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="Edm.String" Nullable="false"/>
</EntityType>
<ComplexType Name="Animal">
</ComplexType>
<ComplexType Name="Human" BaseType="Namespace1_Alias.Animal">
<Property Name="HumanAddress" Type="Microsoft.Exchange.Services.OData.Model.USAddress" />
</ComplexType>
<ComplexType Name="Horse" BaseType="Microsoft.Exchange.Services.OData.Model.Animal">
<Property Name="HorseAddress" Type="Namespace1_Alias.USAddress" />
<Property Name="HorseAddresses" Type="Collection(Microsoft.Exchange.Services.OData.Model.USAddress)" />
</ComplexType>
<ComplexType Name="Address">
<NavigationProperty Name="City" Type="Microsoft.Exchange.Services.OData.Model.City" />
</ComplexType>
<ComplexType Name="USAddress" BaseType="Microsoft.Exchange.Services.OData.Model.Address">
<Property Name="CountryAddress" Type="Microsoft.Exchange.Services.OData.Model.CountryAddress" />
<NavigationProperty Name="SubCity" Type="Microsoft.Exchange.Services.OData.Model.City" />
</ComplexType>
<ComplexType Name="CountryAddress" BaseType="Microsoft.Exchange.Services.OData.Model.Address">
<NavigationProperty Name="SubCity" Type="Microsoft.Exchange.Services.OData.Model.City" />
</ComplexType>
<EntityContainer Name="EntityContainer" m:IsDefaultEntityContainer="true">
<EntitySet Name="Customers" EntityType="Namespace1_Alias.Customer">
<NavigationPropertyBinding Path="Pet/Namespace1_Alias.Human/HumanAddress/SubCity" Target="ODataWebExperimental.OData.Model.EntityContainer1/HumanCities" />
<NavigationPropertyBinding Path="Pet/Microsoft.Exchange.Services.OData.Model.Horse/HorseAddress/SubCity" Target="HorseCities" />
<NavigationPropertyBinding Path="Pet/Microsoft.Exchange.Services.OData.Model.Horse/HorseAddresses/SubCity" Target="HorseCities" />
<NavigationPropertyBinding Path="Microsoft.Exchange.Services.OData.Model.VipCustomer/VipLocations/Microsoft.Exchange.Services.OData.Model.USAddress/SubCity" Target="ODataWebExperimental.OData.Model.EntityContainer1/HumanCities" />
<NavigationPropertyBinding Path="Namespace1_Alias.VipCustomer/VipLocations/Microsoft.Exchange.Services.OData.Model.USAddress/CountryAddress/SubCity" Target="ODataWebExperimental.OData.Model.EntityContainer1/HumanCities" />
<NavigationPropertyBinding Path="Microsoft.Exchange.Services.OData.Model.VipCustomer/VipLocations/City" Target="ODataWebExperimental.OData.Model.EntityContainer1/HumanCities" />
</EntitySet>
<EntitySet Name="HorseCities" EntityType="Microsoft.Exchange.Services.OData.Model.City"/>
</EntityContainer>
</Schema>
<Schema Namespace="ODataWebExperimental.OData.Model" xmlns="http://docs.oasis-open.org/odata/ns/edm" Alias="Namespace2_Alias">
<EntityContainer Name="EntityContainer1" p4:LazyLoadingEnabled="true" xmlns:p4="http://schemas.microsoft.com/ado/2009/02/edm/annotation">
<EntitySet Name="HumanCities" EntityType="Namespace1_Alias.City"/>
<EntitySet Name="Customers" EntityType="Microsoft.Exchange.Services.OData.Model.Customer">
<NavigationPropertyBinding Path="Pet/Microsoft.Exchange.Services.OData.Model.Human/HumanAddress/SubCity" Target="HumanCities" />
<NavigationPropertyBinding Path="Pet/Microsoft.Exchange.Services.OData.Model.Horse/HorseAddress/SubCity" Target="Namespace1_Alias.EntityContainer/HorseCities" />
<NavigationPropertyBinding Path="Pet/Microsoft.Exchange.Services.OData.Model.Horse/HorseAddresses/SubCity" Target="Microsoft.Exchange.Services.OData.Model.EntityContainer/HorseCities" />
<NavigationPropertyBinding Path="Microsoft.Exchange.Services.OData.Model.VipCustomer/VipLocations/Microsoft.Exchange.Services.OData.Model.USAddress/SubCity" Target="HumanCities" />
<NavigationPropertyBinding Path="Microsoft.Exchange.Services.OData.Model.VipCustomer/VipLocations/Microsoft.Exchange.Services.OData.Model.USAddress/CountryAddress/SubCity" Target="HumanCities" />
<NavigationPropertyBinding Path="Microsoft.Exchange.Services.OData.Model.VipCustomer/VipLocations/City" Target="HumanCities" />
</EntitySet>
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>

View File

@ -0,0 +1,355 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
<edmx:DataServices>
<Schema Namespace="Microsoft.OData.SampleService.Models.TripPin" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<EnumType Name="PersonGender">
<Member Name="Male" Value="0" />
<Member Name="Female" Value="1" />
<Member Name="Unknown" Value="2" />
</EnumType>
<ComplexType Name="City">
<Property Name="CountryRegion" Type="Edm.String" Nullable="false" />
<Property Name="Name" Type="Edm.String" Nullable="false" />
<Property Name="Region" Type="Edm.String" Nullable="false" />
</ComplexType>
<ComplexType Name="Location" OpenType="true">
<Property Name="Address" Type="Edm.String" Nullable="false" />
<Property Name="City" Type="Microsoft.OData.SampleService.Models.TripPin.City" Nullable="false" />
</ComplexType>
<ComplexType Name="EventLocation" BaseType="Microsoft.OData.SampleService.Models.TripPin.Location" OpenType="true">
<Property Name="BuildingInfo" Type="Edm.String" />
</ComplexType>
<ComplexType Name="AirportLocation" BaseType="Microsoft.OData.SampleService.Models.TripPin.Location" OpenType="true">
<Property Name="Loc" Type="Edm.GeographyPoint" Nullable="false" SRID="4326" />
</ComplexType>
<EntityType Name="Photo" HasStream="true">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="Edm.Int64" Nullable="false">
<Annotation Term="Org.OData.Core.V1.Permissions">
<EnumMember>Org.OData.Core.V1.Permission/Read</EnumMember>
</Annotation>
</Property>
<Property Name="Name" Type="Edm.String" />
<Annotation Term="Org.OData.Core.V1.AcceptableMediaTypes">
<Collection>
<String>image/jpeg</String>
</Collection>
</Annotation>
</EntityType>
<EntityType Name="Person" OpenType="true">
<Key>
<PropertyRef Name="UserName" />
</Key>
<Property Name="UserName" Type="Edm.String" Nullable="false">
<Annotation Term="Org.OData.Core.V1.Permissions">
<EnumMember>Org.OData.Core.V1.Permission/Read</EnumMember>
</Annotation>
</Property>
<Property Name="FirstName" Type="Edm.String" Nullable="false" />
<Property Name="LastName" Type="Edm.String" Nullable="false" />
<Property Name="Emails" Type="Collection(Edm.String)" />
<Property Name="AddressInfo" Type="Collection(Microsoft.OData.SampleService.Models.TripPin.Location)" />
<Property Name="Gender" Type="Microsoft.OData.SampleService.Models.TripPin.PersonGender" />
<Property Name="Concurrency" Type="Edm.Int64" Nullable="false">
<Annotation Term="Org.OData.Core.V1.Computed" Bool="true" />
</Property>
<NavigationProperty Name="Friends" Type="Collection(Microsoft.OData.SampleService.Models.TripPin.Person)" />
<NavigationProperty Name="Trips" Type="Collection(Microsoft.OData.SampleService.Models.TripPin.Trip)" ContainsTarget="true" />
<NavigationProperty Name="Photo" Type="Microsoft.OData.SampleService.Models.TripPin.Photo" />
</EntityType>
<EntityType Name="Airline">
<Key>
<PropertyRef Name="AirlineCode" />
</Key>
<Property Name="AirlineCode" Type="Edm.String" Nullable="false">
<Annotation Term="Org.OData.Core.V1.Permissions">
<EnumMember>Org.OData.Core.V1.Permission/Read</EnumMember>
</Annotation>
</Property>
<Property Name="Name" Type="Edm.String" Nullable="false" />
</EntityType>
<EntityType Name="Airport">
<Key>
<PropertyRef Name="IcaoCode" />
</Key>
<Property Name="IcaoCode" Type="Edm.String" Nullable="false">
<Annotation Term="Org.OData.Core.V1.Permissions">
<EnumMember>Org.OData.Core.V1.Permission/Read</EnumMember>
</Annotation>
</Property>
<Property Name="Name" Type="Edm.String" Nullable="false" />
<Property Name="IataCode" Type="Edm.String" Nullable="false">
<Annotation Term="Org.OData.Core.V1.Immutable" Bool="true" />
</Property>
<Property Name="Location" Type="Microsoft.OData.SampleService.Models.TripPin.AirportLocation" Nullable="false" />
</EntityType>
<EntityType Name="PlanItem">
<Key>
<PropertyRef Name="PlanItemId" />
</Key>
<Property Name="PlanItemId" Type="Edm.Int32" Nullable="false">
<Annotation Term="Org.OData.Core.V1.Permissions">
<EnumMember>Org.OData.Core.V1.Permission/Read</EnumMember>
</Annotation>
</Property>
<Property Name="ConfirmationCode" Type="Edm.String" />
<Property Name="StartsAt" Type="Edm.DateTimeOffset" />
<Property Name="EndsAt" Type="Edm.DateTimeOffset" />
<Property Name="Duration" Type="Edm.Duration" />
</EntityType>
<EntityType Name="PublicTransportation" BaseType="Microsoft.OData.SampleService.Models.TripPin.PlanItem">
<Property Name="SeatNumber" Type="Edm.String" />
</EntityType>
<EntityType Name="Flight" BaseType="Microsoft.OData.SampleService.Models.TripPin.PublicTransportation">
<Property Name="FlightNumber" Type="Edm.String" Nullable="false" />
<NavigationProperty Name="From" Type="Microsoft.OData.SampleService.Models.TripPin.Airport" Nullable="false" />
<NavigationProperty Name="To" Type="Microsoft.OData.SampleService.Models.TripPin.Airport" Nullable="false" />
<NavigationProperty Name="Airline" Type="Microsoft.OData.SampleService.Models.TripPin.Airline" Nullable="false" />
</EntityType>
<EntityType Name="Event" BaseType="Microsoft.OData.SampleService.Models.TripPin.PlanItem" OpenType="true">
<Property Name="Description" Type="Edm.String" />
<Property Name="OccursAt" Type="Microsoft.OData.SampleService.Models.TripPin.EventLocation" Nullable="false" />
</EntityType>
<EntityType Name="Trip">
<Key>
<PropertyRef Name="TripId" />
</Key>
<Property Name="TripId" Type="Edm.Int32" Nullable="false">
<Annotation Term="Org.OData.Core.V1.Permissions">
<EnumMember>Org.OData.Core.V1.Permission/Read</EnumMember>
</Annotation>
</Property>
<Property Name="ShareId" Type="Edm.Guid" />
<Property Name="Description" Type="Edm.String" />
<Property Name="Name" Type="Edm.String" Nullable="false" />
<Property Name="Budget" Type="Edm.Single" Nullable="false">
<Annotation Term="Org.OData.Measures.V1.ISOCurrency" String="USD" />
<Annotation Term="Org.OData.Measures.V1.Scale" Int="2" />
</Property>
<Property Name="StartsAt" Type="Edm.DateTimeOffset" Nullable="false" />
<Property Name="EndsAt" Type="Edm.DateTimeOffset" Nullable="false" />
<Property Name="Tags" Type="Collection(Edm.String)" Nullable="false" />
<NavigationProperty Name="Photos" Type="Collection(Microsoft.OData.SampleService.Models.TripPin.Photo)" />
<NavigationProperty Name="PlanItems" Type="Collection(Microsoft.OData.SampleService.Models.TripPin.PlanItem)" ContainsTarget="true" />
</EntityType>
<Function Name="GetFavoriteAirline" IsBound="true" EntitySetPath="person/Trips/PlanItems/Microsoft.OData.SampleService.Models.TripPin.Flight/Airline" IsComposable="true">
<Parameter Name="person" Type="Microsoft.OData.SampleService.Models.TripPin.Person" Nullable="false" />
<ReturnType Type="Microsoft.OData.SampleService.Models.TripPin.Airline" Nullable="false" />
</Function>
<Function Name="GetInvolvedPeople" IsBound="true" IsComposable="true">
<Parameter Name="trip" Type="Microsoft.OData.SampleService.Models.TripPin.Trip" Nullable="false" />
<ReturnType Type="Collection(Microsoft.OData.SampleService.Models.TripPin.Person)" Nullable="false" />
</Function>
<Function Name="GetFriendsTrips" IsBound="true" EntitySetPath="person/Friends/Trips" IsComposable="true">
<Parameter Name="person" Type="Microsoft.OData.SampleService.Models.TripPin.Person" Nullable="false" />
<Parameter Name="userName" Type="Edm.String" Nullable="false" />
<ReturnType Type="Collection(Microsoft.OData.SampleService.Models.TripPin.Trip)" Nullable="false" />
</Function>
<Function Name="GetNearestAirport" IsComposable="true">
<Parameter Name="lat" Type="Edm.Double" Nullable="false" />
<Parameter Name="lon" Type="Edm.Double" Nullable="false" />
<ReturnType Type="Microsoft.OData.SampleService.Models.TripPin.Airport" Nullable="false" />
</Function>
<Action Name="ResetDataSource" />
<Action Name="ShareTrip" IsBound="true">
<Parameter Name="person" Type="Microsoft.OData.SampleService.Models.TripPin.Person" Nullable="false" />
<Parameter Name="userName" Type="Edm.String" Nullable="false" />
<Parameter Name="tripId" Type="Edm.Int32" Nullable="false" />
</Action>
<EntityContainer Name="DefaultContainer">
<EntitySet Name="Photos" EntityType="Microsoft.OData.SampleService.Models.TripPin.Photo">
<Annotation Term="Org.OData.Core.V1.ResourcePath" String="Photos" />
<Annotation Term="Org.OData.Capabilities.V1.SearchRestrictions">
<Record>
<PropertyValue Property="Searchable" Bool="true" />
<PropertyValue Property="UnsupportedExpressions">
<EnumMember>Org.OData.Capabilities.V1.SearchExpressions/none</EnumMember>
</PropertyValue>
</Record>
</Annotation>
<Annotation Term="Org.OData.Capabilities.V1.InsertRestrictions">
<Record>
<PropertyValue Property="Insertable" Bool="true" />
<PropertyValue Property="NonInsertableNavigationProperties">
<Collection />
</PropertyValue>
</Record>
</Annotation>
</EntitySet>
<EntitySet Name="People" EntityType="Microsoft.OData.SampleService.Models.TripPin.Person">
<NavigationPropertyBinding Path="Friends" Target="People" />
<NavigationPropertyBinding Path="Microsoft.OData.SampleService.Models.TripPin.Flight/Airline" Target="Airlines" />
<NavigationPropertyBinding Path="Microsoft.OData.SampleService.Models.TripPin.Flight/From" Target="Airports" />
<NavigationPropertyBinding Path="Microsoft.OData.SampleService.Models.TripPin.Flight/To" Target="Airports" />
<NavigationPropertyBinding Path="Photo" Target="Photos" />
<NavigationPropertyBinding Path="Microsoft.OData.SampleService.Models.TripPin.Trip/Photos" Target="Photos" />
<Annotation Term="Org.OData.Core.V1.OptimisticConcurrency">
<Collection>
<PropertyPath>Concurrency</PropertyPath>
</Collection>
</Annotation>
<Annotation Term="Org.OData.Core.V1.ResourcePath" String="People" />
<Annotation Term="Org.OData.Capabilities.V1.NavigationRestrictions">
<Record>
<PropertyValue Property="Navigability">
<EnumMember>Org.OData.Capabilities.V1.NavigationType/None</EnumMember>
</PropertyValue>
<PropertyValue Property="RestrictedProperties">
<Collection>
<Record>
<PropertyValue Property="NavigationProperty" NavigationPropertyPath="Friends" />
<PropertyValue Property="Navigability">
<EnumMember>Org.OData.Capabilities.V1.NavigationType/Recursive</EnumMember>
</PropertyValue>
</Record>
</Collection>
</PropertyValue>
</Record>
</Annotation>
<Annotation Term="Org.OData.Capabilities.V1.SearchRestrictions">
<Record>
<PropertyValue Property="Searchable" Bool="true" />
<PropertyValue Property="UnsupportedExpressions">
<EnumMember>Org.OData.Capabilities.V1.SearchExpressions/none</EnumMember>
</PropertyValue>
</Record>
</Annotation>
<Annotation Term="Org.OData.Capabilities.V1.InsertRestrictions">
<Record>
<PropertyValue Property="Insertable" Bool="true" />
<PropertyValue Property="NonInsertableNavigationProperties">
<Collection>
<NavigationPropertyPath>Trips</NavigationPropertyPath>
<NavigationPropertyPath>Friends</NavigationPropertyPath>
</Collection>
</PropertyValue>
</Record>
</Annotation>
</EntitySet>
<EntitySet Name="Airlines" EntityType="Microsoft.OData.SampleService.Models.TripPin.Airline">
<Annotation Term="Org.OData.Core.V1.ResourcePath" String="Airlines" />
<Annotation Term="Org.OData.Capabilities.V1.SearchRestrictions">
<Record>
<PropertyValue Property="Searchable" Bool="true" />
<PropertyValue Property="UnsupportedExpressions">
<EnumMember>Org.OData.Capabilities.V1.SearchExpressions/none</EnumMember>
</PropertyValue>
</Record>
</Annotation>
<Annotation Term="Org.OData.Capabilities.V1.InsertRestrictions">
<Record>
<PropertyValue Property="Insertable" Bool="true" />
<PropertyValue Property="NonInsertableNavigationProperties">
<Collection />
</PropertyValue>
</Record>
</Annotation>
</EntitySet>
<EntitySet Name="Airports" EntityType="Microsoft.OData.SampleService.Models.TripPin.Airport">
<Annotation Term="Org.OData.Core.V1.ResourcePath" String="Airports" />
<Annotation Term="Org.OData.Capabilities.V1.SearchRestrictions">
<Record>
<PropertyValue Property="Searchable" Bool="true" />
<PropertyValue Property="UnsupportedExpressions">
<EnumMember>Org.OData.Capabilities.V1.SearchExpressions/none</EnumMember>
</PropertyValue>
</Record>
</Annotation>
<Annotation Term="Org.OData.Capabilities.V1.InsertRestrictions">
<Record>
<PropertyValue Property="Insertable" Bool="false" />
<PropertyValue Property="NonInsertableNavigationProperties">
<Collection />
</PropertyValue>
</Record>
</Annotation>
<Annotation Term="Org.OData.Capabilities.V1.DeleteRestrictions">
<Record>
<PropertyValue Property="Deletable" Bool="false" />
<PropertyValue Property="NonDeletableNavigationProperties">
<Collection />
</PropertyValue>
</Record>
</Annotation>
</EntitySet>
<Singleton Name="Me" Type="Microsoft.OData.SampleService.Models.TripPin.Person">
<NavigationPropertyBinding Path="Friends" Target="People" />
<NavigationPropertyBinding Path="Microsoft.OData.SampleService.Models.TripPin.Flight/Airline" Target="Airlines" />
<NavigationPropertyBinding Path="Microsoft.OData.SampleService.Models.TripPin.Flight/From" Target="Airports" />
<NavigationPropertyBinding Path="Microsoft.OData.SampleService.Models.TripPin.Flight/To" Target="Airports" />
<NavigationPropertyBinding Path="Photo" Target="Photos" />
<NavigationPropertyBinding Path="Microsoft.OData.SampleService.Models.TripPin.Trip/Photos" Target="Photos" />
<Annotation Term="Org.OData.Core.V1.ResourcePath" String="Me" />
</Singleton>
<FunctionImport Name="GetNearestAirport" Function="Microsoft.OData.SampleService.Models.TripPin.GetNearestAirport" EntitySet="Airports" IncludeInServiceDocument="true">
<Annotation Term="Org.OData.Core.V1.ResourcePath" String="Microsoft.OData.SampleService.Models.TripPin.GetNearestAirport" />
</FunctionImport>
<ActionImport Name="ResetDataSource" Action="Microsoft.OData.SampleService.Models.TripPin.ResetDataSource" />
<Annotation Term="Org.OData.Core.V1.Description" String="TripPin service is a sample service for OData V4." />
</EntityContainer>
<Annotations Target="Microsoft.OData.SampleService.Models.TripPin.DefaultContainer">
<Annotation Term="Org.OData.Core.V1.DereferenceableIDs" Bool="true" />
<Annotation Term="Org.OData.Core.V1.ConventionalIDs" Bool="true" />
<Annotation Term="Org.OData.Capabilities.V1.ConformanceLevel">
<EnumMember>Org.OData.Capabilities.V1.ConformanceLevelType/Advanced</EnumMember>
</Annotation>
<Annotation Term="Org.OData.Capabilities.V1.SupportedFormats">
<Collection>
<String>application/json;odata.metadata=full;IEEE754Compatible=false;odata.streaming=true</String>
<String>application/json;odata.metadata=minimal;IEEE754Compatible=false;odata.streaming=true</String>
<String>application/json;odata.metadata=none;IEEE754Compatible=false;odata.streaming=true</String>
</Collection>
</Annotation>
<Annotation Term="Org.OData.Capabilities.V1.AsynchronousRequestsSupported" Bool="true" />
<Annotation Term="Org.OData.Capabilities.V1.BatchContinueOnErrorSupported" Bool="false" />
<Annotation Term="Org.OData.Capabilities.V1.FilterFunctions">
<Collection>
<String>contains</String>
<String>endswith</String>
<String>startswith</String>
<String>length</String>
<String>indexof</String>
<String>substring</String>
<String>tolower</String>
<String>toupper</String>
<String>trim</String>
<String>concat</String>
<String>year</String>
<String>month</String>
<String>day</String>
<String>hour</String>
<String>minute</String>
<String>second</String>
<String>round</String>
<String>floor</String>
<String>ceiling</String>
<String>cast</String>
<String>isof</String>
</Collection>
</Annotation>
</Annotations>
</Schema>
</edmx:DataServices>
</edmx:Edmx>