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

Attempting to POST Object with LinkedResourceCollection #121

Closed Croge32 closed 7 years ago

Croge32 commented 7 years ago

First off, this is like my third question so forgive me for being persistent! I am new to iOS so there is a high chance I'm just doing things wrong.

I have an object that takes the following fields on init():

class EventModel: Resource {

  var title: String?
  var startTime: String?
  var endTime: String?
  var descriptionText: String?

  var resources: LinkedResourceCollection?
  var reservations: LinkedResourceCollection?

...

init(title: String?, startTime: String?, endTime: String?, descriptionText: String?, resources: LinkedResourceCollection?) {
    self.title = title
    self.startTime = startTime
    self.endTime = endTime
    self.descriptionText = descriptionText
    self.resources = resources
    super.init()
  }

And in my code, I'm trying to test a save method to make a POST to our API:

let resourceTest = ResourceModel(category: "parking_na", title: "Parking Lot")
    var resources: LinkedResourceCollection?
    resources?.appendResource(resourceTest)
    let eventTest = EventModel(
      title: "Event Blah 4",
      startTime: "2016-11-01T11:00:000Z",
      endTime: "2016-11-01T12:00:000Z",
      descriptionText: "This is an event and stuff 4",
      resources: resources)

    EventAPIHelper.saveEvent(event: eventTest)

The event saves successfully, but my relationships are not properly appearing in the JSON when I get events back from the API:

...
"relationships":{
"reservations":{"data":[]}, // ignore this piece
"resources":{
"data":[]
}
},
...

Am I not implementing the LinkedResourceCollection properly? Because that seems to be the issue.

wvteijlingen commented 7 years ago

LinkedResourceCollection has two methods for adding resources:

The one you want to use in this case is linkResource. However, you cannot link resources that haven't been persisted yet. So you first have to save resourceTest, then add it to the linked collection, and then save eventTest.

stephensilber commented 7 years ago

I'm also struggling with the same issue, and even after linking the resource, there is no concept of included or any attributes within the data tag of the relationship. I have an Order which has multiple Meal objects, but I cannot seem to easily "place an order" which should be making a POST call with the Order object

    init(address: Address, meals: [Meal], couponCode: String?) {
        self.address = address

        let mealsCollection = LinkedResourceCollection(resourcesURL: nil, linkURL: nil, linkage: nil)
        mealsCollection.linkResources(meals)

        self.meals = mealsCollection

        self.coupon = Coupon()
        self.coupon?.code = couponCode

        super.init()
    }
wvteijlingen commented 7 years ago

Hi Stephen, what exactly isn't working for you? Do you mean you want to POST a new Order, while at the same time POSTing the linked meals?

stephensilber commented 7 years ago

Correct, I want to POST an order that also includes relationships. Here is an example of what the request would look like:

{
    "data": {
        "type": "order",
        "attributes": {
            "address_id": 1,
            "payment_id": 1
        },
        "relationships": {
          "address": {
            "data": {
              "id": 1,
              "type": "addresses"
            }
          },
          "payment": {
            "data": {
              "id": "1",
              "type": "payments"
            }
          },
          "order_meals": {
            "data": []
          },
          "meals": {
            "data": [{
                "id": 1,
                "type": "meals",
                "quantity": 1,
                "option_id": 1
            }, {
               "id": 2,
               "type": "meals",
               "quantity": 2
           }]
          },
          "coupon": {
            "data": {
              "code": "voluptates",
              "type": "coupons"
            }
          }
        }
    }
}

Also, I'm aware that attributes aren't really supposed to go inside of the relationships tag, but I don't see a way to put those objects inside of an included key anywhere. I have attempted to addResource and linkResource, but the data for those resources are not included. Since this needs to be a single API call, it's not really possible to save the child relationships before the parent relationship.

If this is not possible, is it possible to use the standalone Serializer to parse a response that includes relationships? I am happy to write a custom outgoing network call if the serializer can parse the response and still delivery a Resource result.

wvteijlingen commented 7 years ago

I'm aware that attributes aren't really supposed to go inside of the relationships tag

This is exactly why Spine has no support for this. The JSON:API standard does not support 'sideposting' resources like this. You have to first save your meals, then link to to the order, and then save the order.

The serializer has no support for this either, sorry.

wvteijlingen commented 7 years ago

Closing this since this is not an issue with the library, but with the JSON:API standard. Feel free to reopen if you think something should be update on Spine :).