wvteijlingen / Spine

A Swift library for working with JSON:API APIs. It supports mapping to custom model classes, fetching, advanced querying, linking and persisting.
MIT License
266 stars 109 forks source link

How can i update a resource? Is it possible? #146

Open sachin-sat opened 7 years ago

sachin-sat commented 7 years ago

Is it possible to update resource to server?

i mean that if i have 'Comment' resource and it's a child of another('Person' resource). Is it possible to add or update a new comment from spine?

also, i would like to change the person's name alone? possible in spine? How can i do it?

Thanks in advance.

sachin-sat commented 7 years ago

also i would like to knwo how can we do pagination with spine? do we have any sample project to have a look? As i'm new to swift and beginner, don't how to work with spine. Thanks in advance.

kurko commented 7 years ago

@sachin-sat

  1. Yes. You create a Comment object. If this object has no id, it will do POST. If it has an id, it will do a PATCH. In servers, PATCH is used to update resources. You could set the id of the object and change the attribute you want to change. I don't think that Spine will update children (Comment belongs to Person). I think you will have to call save() yourself on the child.

    That's a great opportunity to investigate and send PRs to improve the docs if you want to contribute.

  2. README has an example on pagination. Other than that, I recommend you to read the test files. I know it's not the best experience, but that's how I've been learning how to Spine :)

sachin-sat commented 7 years ago

@kurko
Thanks man. I did exactly the same. Reading test files ;-) and constructed the code as below, just have a look! after renamed just name attribute, i try to update the person object to the server.

self.spine.save(person_alan).onSuccess(callback: { (person) in }).onFailure(callback: { (error) in });

Tried saving and always get "The operation couldn’t be completed. (Spine.SpineError error 0.)" in the failure block.

Person Model has two relationships in it(two ToManyRelationship). i dont want to update them. just re-naming the person_alan. Is that any problem? Am i missing anything here?

kurko commented 7 years ago

No way to know. It could be a variety of reasons. Add these 3 lines before the call to save():

Spine.setLogLevel(.Debug, forDomain: .Spine)
Spine.setLogLevel(.Debug, forDomain: .Serializing)
Spine.setLogLevel(.Debug, forDomain: .Networking)

It will output everything that's going on behind the scenes. If that helps you, please open a PR documenting these 3 methods :)

sachin-sat commented 7 years ago

Hey, man! Wow! you are increasing my curiosity level on spine. @kurko

added those logs, and get the following warning.

Debug - Serializing attribute Spine.Attribute as 'name' Warning - No value formatter found for attribute Spine.Attribute. -----> this goes for other attributes and relationships too

the above warning happens when i just change the name attribute of a person.

person.name = "kurko" ;-)

also it states "The operation couldn’t be completed. (Spine.SpineError error 0.)" and UNABLE_TO_PARSE_DATA_TYPE in the error of failure block.

We got errors . So I had a thought to create a new PERSON object with same ID of the object which to be updated.

let newperson = Person.init(id:person_to_be_updated.id) ----> seems good? ;-)

when we do this, it crashes the App as we didn't assign relationship to the new object(new person).

SEE WHAT I DID. ;-) So, i created a new model(UpdatePerson) which has only one attribute(name alone) with same resourcetype(in our case it's person). it has no other attributes and relationships.

let updateperson = UpdatePerson.init(id:person_to_be_updated.id) ----> ;-)

looks funny?, but it works man. Don't know what problems with the model's object. I even didn't register UpdatePerson with spine.

Possible to create model that extends all resources(models that i have) and that can be serialized with minimum of attributes? eg1 : if renaming, it should just have name attribute of a person. eg2 : if updating age, it should just have age attribute of a person.

Any idea on this? but i'm not satisfied in creating new model.

kurko commented 7 years ago

I love your enthusiasm, @sachin-sat :) I don't know what's going on, but looks like either your server is not be responding with correct a jsonapi.org format (because UNABLE_TO_PARSE_DATA_TYPE) or your code is specifying attributes that don't exist?

Can you paste your model code?

sachin-sat commented 7 years ago

:-) thanks man @kurko

Here is my model name as Files. It may be a folder or a file. so it has a 'files' relationship.

@objc class Files : Resource { var created_time: String? var extn: String? var is_folder = false var type:String? var thumbnail_url: String? var created_by: String? var modified_time:String? var opened_time:String? var parent_id:String? var name:String? var download_url:String? var new_badge_count:NSNumber? var status:NSNumber? var files: LinkedResourceCollection? var comments: LinkedResourceCollection? override class var resourceType: String { return "files" } override class var fields: [Field] { return fieldsFromDictionary([ "created_time": Attribute(), "extn": Attribute(), "is_folder": Attribute(), "type": Attribute(), "thumbnail_url":Attribute(), "created_by":Attribute(), "modified_time":Attribute(), "opened_time":Attribute(), "parent_id":Attribute(), "name":Attribute(), "download_url":Attribute(), "new_badge_count":Attribute(), "status":Attribute(), "files": ToManyRelationship(Files.self), "comments": ToManyRelationship(Comments.self), ]) } required init() {super.init()} init(id: String) { super.init() self.id = id } required init(coder: NSCoder) {super.init(coder: coder)} }

Here i would like to just rename a file. so, i will change the name of the File object and try to save it.

marketingFile.name = "Salesforce" spine.save(marketingFile)

since, it's just renaming, i feel it's not good to send opened_time, new_badge_count or any other attributes to the server. I want just serialize the name attribute in spine.possible? This gives me those errors. are other attributes needed while just renaming the file? during serialization?

sachin-sat commented 7 years ago

an another example, you will get it easily. I would like to update new comment to the file. This is what i get in the debug {"data":{"type":"comments","attributes":{"modified_time":null,"created_time":null,"comment":"Hello","contacts_url":null,"resource_id":"fnzpd3c**************"}}}

since it's new comment, there are no values for some attributes except comment and resource_id.

my comments model,

@objc class Comments : Resource { var created_time:String? var modified_time:String? var contacts_url : String? var resource_id:String? var comment : String? override class var resourceType: String { return "comments" } override class var fields: [Field] { return fieldsFromDictionary([ "created_time":Attribute(), "modified_time":Attribute(), "contacts_url":Attribute(), "resource_id":Attribute(), "comment":Attribute() ]) } required init() {super.init()} init(id: String) { super.init() self.id = id } required init(coder: NSCoder) {super.init(coder: coder)} }

It also gives me errors

sachin-sat commented 7 years ago

@kurko , Now, i think i have fixed something. Added .OmittedNullValues helped in our second case,

let newperson = Person.init(id:person_to_be_updated.id) ----> seems good? ;-)

here other attributes are null values except id and name. so they have been omitted. But default bool values are there. Possible to avoid them also? Default values?

While doing this, As i don't work with relationship of a object, it crashes the app. So, i assign the relationship properties to the newperson.

newperson.comments = person_to_be_updated.comments

here i get this,

Info - Cannot resolve relationship 'Optional("comments")' of files:fnzpd3c00d4daebb1444da3c2c0ead0b53f01 because the JSON did not include linkage.

kurko commented 7 years ago

This gives me those errors. [ref]

What errors? What is the return JSON object from your server? What JSON object is being sent to your server?

It also gives me errors [ref]

Again, I can't help unless you show what errors are occurring and what data is being formed (JSON).

Info - Cannot resolve relationship 'Optional("comments")' of files:fnzpd3c00d4daebb1444da3c2c0ead0b53f01 because the JSON did not include linkage.

Judging from the message, this seems to be a problem on the return object. You don't have relationships defined in the example JSON you showed above.

sachin-sat commented 7 years ago

Ouch, sorry @kurko, Has edited it to make understand easily.

@objc class Files : Resource { var is_locked: Any? var extn: String? var type : String? var thumbnail_url: String? var name: String? var modified_time_in_millisecond : NSNumber? var files: LinkedResourceCollection? var permissions: LinkedResourceCollection? var resourceproperties: LinkedResourceCollection? override class var resourceType: String { return "files" } override class var fields: [Field] { return fieldsFromDictionary([ "is_locked": Attribute(), "extn": Attribute(), "type": Attribute(), "thumbnail_url": Attribute(), "name": Attribute(), "modified_time_in_millisecond": Attribute(), //Relationships links "files": ToManyRelationship(Files.self), "permissions": ToManyRelationship(Permissions.self), "resourceproperties": ToManyRelationship(ResourceProperties.self), ]) } required init() {super.init()} init(id: String) { super.init() self.id = id } required init(coder: NSCoder) {super.init(coder: coder)} }

Have a look at this. While saving a object(I create a new object only with 'name' attribute),

let newfile = Files.init(id: renamedFile.id!) spine.save(newfile)

it crashes the App at the point

let resourceCollection = resource.value(forField: relationship.name) as! LinkedResourceCollection

in the execute function of operation.swift file.

unexpectedly found nil while unwrapping an Optional value in the log.

that's the problem of mine.

kurko commented 7 years ago
  1. What's the value of relationship.name? (I guess either files, permissions or resourceproperties)
  2. Perhaps try as?, not as!. If your server returns no relationship, it will be nil and if you use as! it will crash.
sachin-sat commented 7 years ago

@kurko

  1. Yes, they are files, permissions and resourceproperties
  2. (If your server return) - It's working fine while fetching. but while saving(with new object of a file, with just name property and without relationship - means no files,permissions or resourceproperties), it crashes the App. It happens in the execute function of RelationshipMutateOperation class.

thanks in advance

kurko commented 7 years ago

I don't know how to help :( Do you think you're able to send a PR with a failing test to reproduce?