modernistik / parse-stack

Parse Server Ruby Client SDK
https://www.modernistik.com/gems/parse-stack/
MIT License
60 stars 20 forks source link

Adding columns to User #37

Closed gentle-noah closed 7 years ago

gentle-noah commented 7 years ago

Hey there,

I have set up all of my models manually just to observe the process as I add new properties, relationships, pointers, etc...

Every model has worked great except for two. The model I am struggling with currently is my User model.

# The Parse _User collection
class User < Parse::Object
  # Properties
  property :objectId
  property :ACL
  property :authorized
  property :type
  property :email
  property :email_verified, :boolean
  property :username
  property :password
  property :first_name
  property :last_name
  property :companyLogoURL
  property :companyName
  property :zone
  property :color
  property :downloadLocation
  property :zones, :array
  property :createdAt, :date
  property :updatedAt, :date

  # Relations
  has_many :ads, as: :user
  has_many :libraryAds, as: :user
  has_many :reports, as: :user

  # define a before save webhook for Parse::User
  # webhook :before_save do
  #   obj = parse_object # Parse::User
  #   # make changes to record....
  #   obj # will send the proper changelist back to Parse-Server
  # end
end

When I go to query the user, I get back only a select number of columns, despite there being data populated in each column in my parse server.

Example:

user = User.first

Results:

#<Parse::User:0x007fc1bb709ac0 @id="NhwuGPCzjt", @email="xxxxxx@gmail.com", @username="xxxxxx", @created_at=Wed, 28 Jun 2017 22:49:11 +0000, @updated_at=Wed, 28 Jun 2017 22:51:08 +0000, @acl=ACL({"*"=>{"read"=>true}, "NhwuGPCzjt"=>{"read"=>true, "write"=>true}}), @previously_changed={}, @changed_attributes={}>

Is there something I am missing to get the added columns to populate in the result?

apersaud commented 7 years ago

The issue is that you declared a class 'User' but this class is already defined as 'Parse::User' (which is the one you are querying). I would also add that there are several defined properties in your model that do not have to be there since they are standard in the User collection (Parse::User).

# Parse::User already defined with properties, you only
# need to add the ones specific to your logic
class Parse::User < Parse::Object
  # Properties 
  property :authorized
  property :type
  property :first_name
  property :last_name
  property :companyLogoURL
  property :companyName
  property :zone
  property :color
  property :downloadLocation
  property :zones, :array

  # Relations
  has_many :ads, as: :user
  has_many :libraryAds, as: :user
  has_many :reports, as: :user

end

Parse.auto_upgrade!

After you make the change and run Parse.auto_upgrade! (to make sure you schema is up to date, try your query again:

user = Parse::User.first
gentle-noah commented 7 years ago

Thanks for the quick response @apersaud

Just to verify before running: Parse.auto_upgrade! is non destructive, correct? I have about 150 users stored in this parse instance and can't mess up any of the existing data.

apersaud commented 7 years ago

I would always recommend to try out anything, especially when working with a new framework, in your development environment as well as always having backups of your production data. I also highly recommend you review the guide we've put together as I noticed that there seems to be a few mistakes in your model setup that we cover in that documentation. Let me know if that helps.

But to answer the question, auto upgrading is a non-descrtructive operation. If in doubt, you can always modify your schema manually and not use Parse.auto_upgrade! for additional safety.

gentle-noah commented 7 years ago

@apersaud - I ran through your suggestions and no luck for me, yet. Sorry if I'm being obtuse here. Simultaneously learning Parse and your library. I'm trying to make sense of a very brutal code base and am trying to port it over to Rails so some basic things like authentication (working already), etc... can get stood up quickly.

Still the same result when I run Parse::User.first. One additional thing to note though... the following query brings about the desired results: Parse::User.where(type: 'company') even though I can't access the type param on user = Parse::User.first

The good news is that Parse.auto_upgrade! is totally non-destructive :)

gentle-noah commented 7 years ago

So I have reduced the User model to the following:

class Parse::User < Parse::Object
  # Properties
  property :first_name
end

Then I run:

user = Parse::User.first

And receive the following result:

#<Parse::User:0x007fc1b7db8cd0 @id="iYK6SSNcB5", @email="test@gmail.com", @username="test", @created_at=Wed, 31 Aug 2016 23:05:26 +0000, @updated_at=Fri, 30 Jun 2017 23:09:07 +0000, @acl=ACL({"*"=>{"read"=>true}, "iYK6SSNcB5"=>{"read"=>true, "write"=>true}}), @previously_changed={}, @changed_attributes={}>

Then if I run:

user.firstName

I get the following:

NoMethodError: undefined method `firstName' for #<Parse::User:0x007fc1b7d96f40>

Evidence of the column existing in my Parse Server instance:

screen shot 2017-06-30 at 8 07 07 pm

I read through all of the documentation talking about the Parse::User specifically and still can't seem to find an answer as to why I cannot get my added columns available via my rails console or else where. This is the only model I am having this issue with. Every other model works 100% awesomely.

I can however query using the added columns:

2.3.1 :005 > Parse::User.where(firstName: 'Noah').count
 => 2

2.3.1 :009 > Parse::User.where(first_name: 'Noah').count
 => 2
apersaud commented 7 years ago

A few things to try:

Let's try first using parse-console to see if your classes can be easily imported (use master key).

$ parse-console -v -a YOUR_APP_ID -m YOUR_MASTER_KEY -s http://yourserver/parse
Server : http://yourserver/parse
App Id : YOUR_APP_ID
Master : true
2.4.0 > Parse::User.fields # get it from local detection
2.4.0 > Parse::User.schema # get it from parse-server

When you call Parse::User.fields you can see the mapping of the columns that it was able to retrieve. If your firstName field is not there, then there might be something wrong on the parse-server side where the schema is not being provided.

Another thing to try is use the debugger to see if the field was added properly. At least a few techniques below might help you debug what is going on.

 require 'parse-stack'
 class Parse::User < Parse::Object
    property :first_name
 end

 Parse::User.fields
# => {:id=>:string, :created_at=>:date, :updated_at=>:date, 
#      :acl=>:acl, :auth_data=>:object, :authData=>:object, 
#       :email=>:string, :password=>:string, :username=>:string, 
#      :first_name=>:string, :firstName=>:string} 

user = Parse::User.new
user.first_name = 'Anthony'
user.first_name # => Anthony
user.firstName # => Anthony

Also, I'm assuming you are using the latest version 1.7.0?

gentle-noah commented 7 years ago

When trying to access the parse-console, I got the following:

.rvm/gems/ruby-2.3.1/gems/parse-stack-1.7.0/lib/parse/model/core/builder.rb:51:in `const_set': wrong constant name _PushStatus (NameError)

So I moved on to the second suggestion. When setting the model in rails console, first_name was properly added. I even queried other users and the first_name popped up with each one. When checking Parse::User.fields I received the expected result that you presented.

So, I tried closing out the console opening it back up and querying a few users and the first_name param was gone. This is leading me to believe that the issue is somewhere in the initialization? Maybe specific to Parse::User?

apersaud commented 7 years ago

What version of Parse-Server are you running?

gentle-noah commented 7 years ago

Parse-Server: 2.2.12 Rails: 5.1.2 Ruby: 2.3.1 Parse-Stack: 1.7.0

gentle-noah commented 7 years ago

Adding my config/initializers/parse.rb just in case:

require 'parse/stack'

Parse.setup(
  app_id: ENV['PARSE_APP_ID'],
  master_key: ENV['PARSE_MASTER_KEY'],
  server_url: 'http://xxxxxxxxx.xxxxxxxxx.com/parse'
  # logging: false,
  # cache: Moneta.new(:File, dir: 'tmp/cache'),
  # expires: 1 # cache ttl 1 second
)
gentle-noah commented 7 years ago

Update:

When I paste the class into my console, I am able to access all properties just fine. Definitely think it has to do with the Parse::User class instantiation.

Interesting behavior observed - I recreated the class like so:

# The Parse _User collection
module Parse
  class User < Parse::Object
    parse_class Parse::Model::CLASS_USER

    #Properties
    property :authorized
    property :type
    property :first_name
    property :last_name
    property :companyLogoURL
    property :companyName
    property :zone
    property :color
    property :downloadLocation
    property :zones, :array

    # Relationships
    has_many :ads, as: :user
    has_many :libraryAds, as: :user
    has_many :reports, as: :user
  end
end

I then booted up my console. If I call Parse::User.first I get the same old bunk result. BUT - If I call User, I get the following error:

2.3.1 :001 > User
LoadError: Unable to autoload constant User, expected /Users/noahdavis/Code/nomad/admin/app/models/user.rb to define it
    from (irb):1

THEN - when I call Parse::User.first, I get my full desired result with all of the properties defined in my model class.

gentle-noah commented 7 years ago

Update:

After some brutal hacking, I have something that works -- though very ugly:

# The Parse _User collection
class User < Parse::Object
  parse_class Parse::Model::CLASS_USER

  class Parse::User < Parse::Object
    #Properties
    property :authorized
    property :type
    property :first_name
    property :last_name
    property :companyLogoURL
    property :companyName
    property :zone
    property :color
    property :downloadLocation
    property :zones, :array

    # Relationships
    has_many :ads, as: :user
    has_many :libraryAds, as: :user
    has_many :reports, as: :user
  end
end
apersaud commented 7 years ago

From the looks of it, I believe there is something in your Rails app configuration that might be interferring with your models. Technically you should be able to do:

  class Parse::User < Parse::Object
    #Properties
    property :authorized
    property :type
    property :first_name
    property :last_name
    property :companyLogoURL
    property :companyName
    property :zone
    property :color
    property :downloadLocation
    property :zones, :array

    # Relationships
    has_many :ads, as: :user
    has_many :libraryAds, as: :user
    has_many :reports, as: :user
  end

User = Parse::User

and it should achieve the same results.

apersaud commented 7 years ago

Given that this doesn't seem to be a parse-stack specific bug without a reproducible example app (and could be a multitude of many other things), I'm going to close the issue for now.