softlayer / softlayer-ruby

http://softlayer.github.io/softlayer-ruby/
MIT License
54 stars 35 forks source link

Error in upgrading from 1.0.8 to 2.2.0 #54

Closed wzymaster closed 9 years ago

wzymaster commented 9 years ago

I was using 1.0.8 gem for my rails app, however I found that API calls are breaking after upgrade from 1.0.8 to 2.2.0 gem.

My existing codes look like below :

@softlayer_product_package = SoftLayer::Service.new("SoftLayer_Product_Package",

In order to accommodate 2.2.0, I have updated codes to

@client |= SoftLayer::Client.new( username: @sl_user_name, # enter your username here api_key: @sl_api_key # enter your api key here ) package = SoftLayer::ProductPackage.virtual_server_package(@client) available_disks = package.getAvailableStorageUnits all_categories = package.object_mask("itemCategory","isRequired").getConfiguration item_prices = package.object_mask("id", "item.description", "categories.id", "isRequired").getItemPrices

but I got errors like below :

NoMethodError: undefined method `getAvailableStorageUnits' for #SoftLayer::ProductPackage:0x007fe4d497fdb8

NoMethodError: undefined method `object_mask' for #SoftLayer::ProductPackage:0x007fe4d4b77e40

Can you provide some examples on how I can upgrade my rails app to accommodate 2.2.0 here ?

SLsthompson commented 9 years ago

The value returned by

SoftLayer::ProductPackage.virtual_server_package(@client)

is not a service object, but a Ruby object in the Model Layer (http://softlayer.github.io/softlayer-ruby/doc/Model%20Layer_md.html) representing the Package (http://softlayer.github.io/softlayer-ruby/doc/SoftLayer/ProductPackage.html)

As such it responds to a limited set of methods itself. Some of the information you are requesting is available from that object using the "configuration()" method and the features of the SoftLayer::ProductItemCategory class (http://softlayer.github.io/softlayer-ruby/doc/SoftLayer/ProductItemCategory.html).

However if you simply wish to translate your code so that it calls through to the network API, ask the ProductPackage object for it's "service":

available_disks = package.service.getAvailableStorageUnits
all_categories = package.service.object_mask("itemCategory","isRequired").getConfiguration
item_prices = package.service.object_mask("id", "item.description", "categories.id", "isRequired").getItemPrices
wzymaster commented 9 years ago

thanks for the quick response, however I still get error for object mask like below :

2.0.0-p481 :006 > all_categories = pkg.service.object_mask("itemCategory","isRequired").getConfiguration SoftLayer::ObjectMaskParserError: Object Mask must begin with a 'mask' or 'filterMask' root property

SLsthompson commented 9 years ago

Oh... sorry. Object masks must now be formatted using the "Extended Object Mask" format (http://sldn.softlayer.com/article/Object-Masks)

In this case, your mask should be object_mask("mask[itemCategory, isRequired]")

The mask on item_prices will have to be adjusted similarly.

wzymaster commented 9 years ago

thanks for your quick reply. Yes, it works for me now after using the new mask format, however, I got error like below for item_prices :

2.0.0-p481 :009 > item_prices = pkg.service.object_mask("mask[id, item.description, categories.id, isRequired]").getItemPrices XMLRPC::FaultException: Property 'isRequired' not valid for 'SoftLayer_Product_Item_Price'.

I am wondering if 'isRequired' property is removed from new API ?

SLsthompson commented 9 years ago

The low-level networking API (the one you get to by talking to services directly) has not changed. Version 2 (and later) of the softlayer_api gem builds high-level classes on top of the existing network API.

One of those changes is the use of "extended" object masks instead of the old-style object masks. Looking in SLDN, a "SoftLayer_Product_Item_Price" has never had the 'isRequired' property. However, the extended object masks are more strict about validating properties. Asking for that property in that mask has always been invalid... but it was not checked in the past.

It might be interesting to see what you are doing with the item prices you are getting back from the API with these calls. It seems like you're doing a lot of the same work that some of the high level classes could be doing for you.

wzymaster commented 9 years ago

Yeah, what I am doing here is to retrieve all required category and associated item prices and build a local cache of packages.

Do you have any samples of high level classes to help me achieve that goal instead of using the low-level API calls ?

SLsthompson commented 9 years ago

Have you tried looking at the bare metal package ordering example:

https://github.com/softlayer/softlayer-ruby/blob/master/examples/order_bare_metal_package.rb

It is concerned with ordering a bare metal server, but it uses the high-level package classes to do some of the work.

SLsthompson commented 9 years ago

Here's another example:

require 'softlayer_api'
require 'pp'

SoftLayer::Client.default_client = SoftLayer::Client.new()
package = SoftLayer::ProductPackage.virtual_server_package()
required_categories = package.configuration()

required_categories.each { |category|
  puts "#{category.name}"

  category.configuration_options.each { |item|
    puts "\t#{item.description}"
  }
}

(Note that I have a ".softlayer" configuration file in my home directory so I don't have to provide separate credentials when creating a client)

wzymaster commented 9 years ago

thanks very much. I am wondering if low level functions are supported in the enhanced SoftLayer API gem ?

like , verify order with one or multiple disk RAIDs ? Any example for that ?

SLsthompson commented 9 years ago

Oh sure. You can still get at all the low level stuff. You don't need to use the high-level interfaces at all if you don't want to.

If you want to use your original code you could get the SoftLayer_Product_Package service using:

@client = # code you have above for creating the client
@softlayer_product_package = @client[:Product_Package]  # note that specifying "SoftLayer_" is not required

At that point I think the rest of your code would be unchanged... except needing to translate your object masks to extended format.

SLsthompson commented 9 years ago

At the same time, if you want to use the high level interfaces (SoftLayer::VirtualServerOrder for example) there are ways to plug into the ordering system (verify and place_order! accept blocks that can modify the order templates) so you could add things like RAID systems to the orders without having the directly supported by the order objects.

wzymaster commented 9 years ago

according to your suggested sample, it isn't an object ? @softlayer_product_package = @client[:Product_Package]

2.0.0-p481 :010 > softlayer_product_package.getItemPrices XMLRPC::FaultException: Object does not exist to execute method on. (SoftLayer_Product_Package::getItemPrices)

SLsthompson commented 9 years ago

Sorry. I shouldn't type code into the browser without trying it "for real". Here's your original code translated using the low-level API functionality:

require 'softlayer_api'

client = SoftLayer::Client.new(
# username: @sl_user_name
# api_key: @sl_api_key
)

softlayer_product_package = client[:Product_Package]
package = softlayer_product_package.object_with_id 46

available_disks = package.getAvailableStorageUnits 
all_categories = package.object_mask("mask[itemCategory,isRequired]").getConfiguration 
item_prices = package.object_mask("mask[id,item.description,categories.id]").getItemPrices

puts available_disks
puts all_categories.inspect
puts item_prices.inspect
wzymaster commented 9 years ago

thanks for your quick response. In my understanding , @client[:Product_Package] is kind of initialization for softlayer_product_pakcage service , which is equal to the below ?

SoftLayer::Service.new("SoftLayer_Product_Package", username: @sl_user_name, api_key: @sl_api_key)

The similar rule is applied to other services , like SoftLayer_Product_Order, SoftLayer_Account, SoftLayer_Security_Ssh_Key , SoftLayer_Virtual_Guest, SoftLayer_Hardware ?

SLsthompson commented 9 years ago

Yes. Any service can (and should) be created through the client. You may use brackets as shown, or you can call the "servicenamed" method. You can provide strings or symbols and adding "SoftLayer" on the front is optional.

SLsthompson commented 9 years ago

It seems that you're back on track so I'm going to close this issue. Please feel free to get back if you have further trouble.

wzymaster commented 9 years ago

thanks very much !

wzymaster commented 9 years ago

@SLsthompson when will you guys release 3.0 ? very soon ?

SLsthompson commented 9 years ago

We have some folks that are looking at the 3.0 release to make sure there are no problems. If you'd like to try it yourself you should be able to do 'gem install softlayer_api -v "3.0.b2" --pre' (or you can grab the v.3.0 branch and "rake gem; gem install pkg/softlayer_api-3.0.b2.gem")

Once the internal review is done we'll look at doing a full-blown release. Should be next week, I hope.