Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add build_id_path for Strings #72

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 34 additions & 14 deletions lib/ruby_odata/service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -180,40 +180,60 @@ def build_collection_query_object(name, additional_parameters, *args)
if args.empty?
#nothing to add
elsif args.size == 1
if args.first.to_s =~ /\d+/
id_metadata = find_id_metadata(name.to_s)
root << build_id_path(args.first, id_metadata)
else
root << "(#{args.first})"
end
keys = get_keys_metadata(name.to_s)
root << "(#{build_ids(keys, args.first).join(',')})"
else
root << "(#{args.join(',')})"
end
QueryBuilder.new(root, additional_parameters)
end

# Finds the metadata associated with the given collection's first id property
# Remarks: This is used for single item lookup queries using the ID, e.g. Products(1), not complex primary keys
# Finds the metadata associated with the given collection's keys
#
# @param [String] collection_name the name of the collection
def find_id_metadata(collection_name)
def get_keys_metadata(collection_name)
collection_data = @collections.fetch(collection_name)
class_metadata = @class_metadata.fetch(collection_data[:type].to_s)
key = class_metadata.select{|k,h| h.is_key }.collect{|k,h| h.name }[0]
class_metadata[key]
keys = class_metadata.select{|k,h| h.is_key }
end

# Builds the ID expression of a given id for query
#
# @param [Object] id_value the actual value to be used
# @param [PropertyMetadata] id_metadata the property metadata object for the id
# Builds the IDs expression for the given ids for query
#
# @param [Hash] keys Hash of metadata for the keys
# @param [Object/Hash] values
def build_ids(keys, values)
if keys.size == 1
[ quote_id(values, keys.first[1]) ]
elsif values.is_a?(Hash)
ids = []
keys.each_pair do |key, meta|
v = values[key.to_sym]
ids << "#{key}=#{quote_id(v, meta)}"
end
ids
else
values.to_a
end
end

# Builds the ID expression of a given id for query
#
# @param [Object] id_value the actual value to be used
# @param [PropertyMetadata] id_metadata the property metadata object for the id
def build_id_path(id_value, id_metadata)
def quote_id(id_value, id_metadata)
if id_metadata.type == "Edm.Int64"
"(#{id_value}L)"
"#{id_value}L"
elsif id_metadata.type == "Edm.String"
"'#{id_value}'"
else
"(#{id_value})"
"#{id_value}"
end
end


def set_options!(options)
@options = options
Expand Down
75 changes: 75 additions & 0 deletions spec/dynamics_nav_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
require 'spec_helper'

module OData
describe Service do

describe "handling of Microsoft Dynamics Nav OData WebService" do
let(:username) { "blabla" }
let(:password) { "" }

before(:each) do
auth_string = "#{username}:#{password}"
authorization_header = { authorization: "Basic #{Base64::encode64(auth_string).strip}" }
headers = DEFAULT_HEADERS.merge(authorization_header)

# Required for the build_classes method
stub_request(:get, "http://test.com/nav.svc/$metadata").
with(:headers => headers).
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/ms_dynamics_nav/edmx_ms_dynamics_nav.xml", __FILE__)), :headers => {})

stub_request(:get, "http://test.com/nav.svc/Customer").
with(:headers => headers).
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/ms_dynamics_nav/result_customer.xml", __FILE__)), :headers => {})

stub_request(:get, "http://test.com/nav.svc/Customer('100013')").
with(:headers => headers).
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/ms_dynamics_nav/result_customer.xml", __FILE__)), :headers => {})

stub_request(:get, "http://test.com/nav.svc/Customer(100013)").
with(:headers => headers).
to_return(:status => 400, :body => File.new(File.expand_path("../fixtures/ms_dynamics_nav/result_customer_error.xml", __FILE__)), :headers => {})

stub_request(:get, "http://test.com/nav.svc/SalesOrder(Document_Type='Order',No='AB-1600013')").
with(:headers => headers).
to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/ms_dynamics_nav/result_sales_order.xml", __FILE__)), :headers => {})
end

after(:all) do
Object.send(:remove_const, 'Customer')
Object.send(:remove_const, 'SalesOrder')
end

it "should successfully parse null valued string properties" do
svc = OData::Service.new "http://test.com/nav.svc/", { :username => username, :password => password, :verify_ssl => false }
svc.Customer
results = svc.execute
results.first.should be_a_kind_of(Customer)
end

it "should successfully return a customer by its string id" do
svc = OData::Service.new "http://test.com/nav.svc/", { :username => username, :password => password, :verify_ssl => false }
svc.Customer('100013')
results = svc.execute
results.first.should be_a_kind_of(Customer)
results.first.Name.should eq 'Contoso AG'
end

it "should cast to string if a customer is accessed with integer id" do
svc = OData::Service.new "http://test.com/nav.svc/", { :username => username, :password => password, :verify_ssl => false }
svc.Customer(100013)
results = svc.execute
results.first.should be_a_kind_of(Customer)
results.first.Name.should eq 'Contoso AG'
end

it "should successfully return a sales_order by its composite string ids" do
svc = OData::Service.new "http://test.com/nav.svc/", { :username => username, :password => password, :verify_ssl => false }
svc.SalesOrder(Document_Type: 'Order', No: 'AB-1600013')
results = svc.execute
results.first.should be_a_kind_of(SalesOrder)
results.first.No.should eq 'AB-1600013'
end

end
end
end
92 changes: 92 additions & 0 deletions spec/fixtures/ms_dynamics_nav/edmx_ms_dynamics_nav.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<edmx:Edmx
xmlns:edmx="http://schemas.microsoft.com/ado/2007/06/edmx" Version="1.0">
<edmx:DataServices
xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" m:DataServiceVersion="1.0" m:MaxDataServiceVersion="3.0">
<Schema
xmlns="http://schemas.microsoft.com/ado/2007/05/edm" Namespace="NAV">
<EntityType Name="Customer">
<Key>
<PropertyRef Name="No"/>
</Key>
<Property Name="No" Type="Edm.String" Nullable="false"/>
<Property Name="Name" Type="Edm.String"/>
<Property Name="Address" Type="Edm.String"/>
<Property Name="Address_2" Type="Edm.String"/>
<Property Name="Post_Code" Type="Edm.String"/>
<Property Name="City" Type="Edm.String"/>
<Property Name="Country_Region_Code" Type="Edm.String"/>
<Property Name="ETag" Type="Edm.String" ConcurrencyMode="Fixed"/>
</EntityType>
<EntityType Name="SalesOrder">
<Key>
<PropertyRef Name="Document_Type"/>
<PropertyRef Name="No"/>
</Key>
<Property Name="Document_Type" Type="Edm.String" Nullable="false"/>
<Property Name="No" Type="Edm.String" Nullable="false"/>
<Property Name="Sell_to_Customer_No" Type="Edm.String"/>
<Property Name="ETag" Type="Edm.String" ConcurrencyMode="Fixed"/>
<NavigationProperty Name="SalesOrderSalesLines" Relationship="NAV.SalesOrder_SalesOrderSalesLines" ToRole="SalesOrderSalesLines" FromRole="SalesOrder"/>
</EntityType>
<EntityType Name="SalesOrderSalesLines">
<Key>
<PropertyRef Name="Document_No"/>
<PropertyRef Name="Document_Type"/>
<PropertyRef Name="Line_No"/>
</Key>
<Property Name="Document_Type" Type="Edm.String" Nullable="false"/>
<Property Name="Document_No" Type="Edm.String" Nullable="false"/>
<Property Name="Line_No" Type="Edm.Int32" Nullable="false"/>
<Property Name="Position_No" Type="Edm.Int32"/>
<Property Name="Type" Type="Edm.String"/>
<Property Name="No" Type="Edm.String"/>
</EntityType>
<EntityType Name="Company">
<Key>
<PropertyRef Name="Name"/>
</Key>
<Property Name="Name" Type="Edm.String" Nullable="false"/>
<NavigationProperty Name="Customer" Relationship="NAV.Company_Customer" ToRole="Customer" FromRole="Company"/>
<NavigationProperty Name="SalesOrder" Relationship="NAV.Company_SalesOrder" ToRole="SalesOrder" FromRole="Company"/>
<NavigationProperty Name="SalesOrderSalesLines" Relationship="NAV.Company_SalesOrderSalesLines" ToRole="SalesOrderSalesLines" FromRole="Company"/>
</EntityType>
<Association Name="SalesOrder_SalesOrderSalesLines">
<End Type="NAV.SalesOrderSalesLines" Role="SalesOrderSalesLines" Multiplicity="*"/>
<End Type="NAV.SalesOrder" Role="SalesOrder" Multiplicity="*"/>
</Association>
<Association Name="Company_Customer">
<End Type="NAV.Customer" Role="Customer" Multiplicity="*"/>
<End Type="NAV.Company" Role="Company" Multiplicity="*"/>
</Association>
<Association Name="Company_SalesOrder">
<End Type="NAV.SalesOrder" Role="SalesOrder" Multiplicity="*"/>
<End Type="NAV.Company" Role="Company" Multiplicity="*"/>
</Association>
<Association Name="Company_SalesOrderSalesLines">
<End Type="NAV.SalesOrderSalesLines" Role="SalesOrderSalesLines" Multiplicity="*"/>
<End Type="NAV.Company" Role="Company" Multiplicity="*"/>
</Association>
<EntityContainer Name="NAV" m:IsDefaultEntityContainer="true">
<EntitySet Name="Customer" EntityType="NAV.Customer"/>
<EntitySet Name="SalesOrder" EntityType="NAV.SalesOrder"/>
<EntitySet Name="SalesOrderSalesLines" EntityType="NAV.SalesOrderSalesLines"/>
<AssociationSet Name="SalesOrder_SalesOrderSalesLines_SalesOrderSalesLines" Association="NAV.SalesOrder_SalesOrderSalesLines">
<End Role="SalesOrder" EntitySet="SalesOrder"/>
<End Role="SalesOrderSalesLines" EntitySet="SalesOrderSalesLines"/>
</AssociationSet>
<AssociationSet Name="Company_Customer_Customer" Association="NAV.Company_Customer">
<End Role="Company" EntitySet="Company"/>
<End Role="Customer" EntitySet="Customer"/>
</AssociationSet>
<AssociationSet Name="Company_SalesOrder_SalesOrder" Association="NAV.Company_SalesOrder">
<End Role="Company" EntitySet="Company"/>
<End Role="SalesOrder" EntitySet="SalesOrder"/>
</AssociationSet>
<AssociationSet Name="Company_SalesOrderSalesLines_SalesOrderSalesLines" Association="NAV.Company_SalesOrderSalesLines">
<End Role="Company" EntitySet="Company"/>
<End Role="SalesOrderSalesLines" EntitySet="SalesOrderSalesLines"/>
</AssociationSet>
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>
26 changes: 26 additions & 0 deletions spec/fixtures/ms_dynamics_nav/result_customer.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<entry xml:base="http://navapp1.office.zwick-edelstahl.de:7178/ZWICK-TEST-WEB/OData/"
xmlns="http://www.w3.org/2005/Atom"
xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices"
xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" m:etag="W/&quot;'28%3BEgAAAAJ7BTEAMAAwADAAMAAAAAAA8%3B791241770%3B'&quot;">
<id>http://navapp1.office.zwick-edelstahl.de:7178/ZWICK-TEST-WEB/OData/Customer('10000')</id>
<category term="NAV.Customer" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
<link rel="edit" title="Customer" href="Customer('10000')" />
<title />
<updated>2017-08-02T20:02:42Z</updated>
<author>
<name />
</author>
<content type="application/xml">
<m:properties>
<d:No>10000</d:No>
<d:Name>Contoso AG</d:Name>
<d:Address></d:Address>
<d:Address_2></d:Address_2>
<d:Post_Code></d:Post_Code>
<d:City>Berlin</d:City>
<d:Country_Region_Code>DE</d:Country_Region_Code>
<d:ETag>28;EgAAAAJ7BTEAMAAwADAAMAAAAAAA8;791241770;</d:ETag>
</m:properties>
</content>
</entry>
5 changes: 5 additions & 0 deletions spec/fixtures/ms_dynamics_nav/result_customer_error.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<m:error
xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
<m:code/>
<m:message xml:lang="de-DE">Bad Request - Error in query syntax.</m:message>
</m:error>
24 changes: 24 additions & 0 deletions spec/fixtures/ms_dynamics_nav/result_sales_order.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<entry xml:base="http://navapp1.office.zwick-edelstahl.de:7178/ZWICK-TEST-WEB/OData/"
xmlns="http://www.w3.org/2005/Atom"
xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices"
xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" m:etag="W/&quot;'52%3BJAAAAACLAQAAAAJ7%2F0EAQgAtADEANgAwADAAMAAxADMAAAAAAA%3D%3D8%3B824184700%3B'&quot;">
<id>http://navapp1.office.zwick-edelstahl.de:7178/ZWICK-TEST-WEB/OData/SalesOrder(Document_Type='Order',No='AB-1600013')</id>
<category term="NAV.SalesOrder" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
<link rel="edit" title="SalesOrder" href="SalesOrder(Document_Type='Order',No='AB-1600013')" />
<link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/SalesOrderSalesLines" type="application/atom+xml;type=feed" title="SalesOrderSalesLines" href="SalesOrder(Document_Type='Order',No='AB-1600013')/SalesOrderSalesLines" />
<link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/SalesOrderRouting_Status_View" type="application/atom+xml;type=feed" title="SalesOrderRouting_Status_View" href="SalesOrder(Document_Type='Order',No='AB-1600013')/SalesOrderRouting_Status_View" />
<title />
<updated>2017-08-02T19:53:22Z</updated>
<author>
<name />
</author>
<content type="application/xml">
<m:properties>
<d:Document_Type>Order</d:Document_Type>
<d:No>AB-1600013</d:No>
<d:Sell_to_Customer_No>11101</d:Sell_to_Customer_No>
<d:ETag>52;JAAAAACLAQAAAAJ7/0EAQgAtADEANgAwADAAMAAxADMAAAAAAA==8;824184700;</d:ETag>
</m:properties>
</content>
</entry>