Coder Social home page Coder Social logo

jsonapi-rust's Introduction

jsonapi-rust

Build Status Crates.io Status License Documentation FOSSA Status

This is an implementation of the JSON-API v1 specification at jsonapi.org.

Use

Add this crate to your Cargo.toml file,

[dependencies]
jsonapi = "*"

Or use the master branch directly from github,

[dependencies]
jsonapi = { git = "https://github.com/michiel/jsonapi-rust", branch = "master" }

Examples of most serialization and deserialization cases can be found in the tests/ directory or the documentation.

Development

Note - Until this crate reaches v1.0.0 breaking changes that are not backwards compatible will be announced in the CHANGELOG.

Testing

The command cargo test will run all tests. For more verbose output or output with cargo watch,

RUST_BACKTRACE=1 cargo test -- --nocapture
RUST_BACKTRACE=1 cargo watch "test -- --nocapture"

Contributing

Contributions are welcome. Please add tests and write commit messages using using conventional format. The Changelog is updated using the clog tool. The configuration is found in .clog.toml.

The current configuration works for commit messages prefixed with feat:, bug:, test:, doc: and refactor:.

License

FOSSA Status

jsonapi-rust's People

Contributors

antage avatar dependabot-preview[bot] avatar dependabot-support avatar dependabot[bot] avatar dopin avatar fossabot avatar guidorice avatar jadedevan avatar michiel avatar mmstick avatar nubis avatar phansch avatar pka avatar therustmonk avatar ttdonovan avatar w-k-s avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

jsonapi-rust's Issues

cannot find macro `jsonapi_model!` in this scope

I'm looking for a little guidance on what would seem to be a very contrived and easy example. I have an extremely bare-bones application defined in src/main.rs as follows:

#[macro_use] extern crate jsonapi;

use jsonapi::model::*;

#[derive(Debug)]
pub struct Person {
    pub id: i32,
    pub name: String
}

jsonapi_model!(Person; "person");

fn main() {
    let user = Person { id: 1, name: "World".to_string() };
    println!("{:?}", user);
}

Upon compilation I receive the following error

cannot find macro `jsonapi_model!` in this scope

What am I missing here? I feel like I've copied the necessary includes and macro imports (based on what I was seeing in the tests directory) yet I cannot get this to compile. I am running rustc 1.22.1. Any help would be greatly appreciated.

QueryParams, Links, Pagination, and Included Serialization

Hi there! Great library, I had a few questions after reading the docs and reading through some of the test cases. Some things aren't immediately apparent so I'd love some insight into this or some recommendations.

  • What's the recommended way to construct links? If I use the jsonapi_model! macro there doesn't seem to be a good way of shoving in additional links without some additional work (such as spreading out the document into a new document). If that's the recommended approach then I'd love to see some docs around that (willing to help!)
  • Same question as above for pagination attributes
  • What's the recommended way to not include models from a relationship in the included field? If the consumer hasn't specified the "include" parameter then I don't want to expand the response but I don't think I care if I overfetch to get the information for the relationship. In other words, if I provide the nested struct, I don't care that it gets filtered out at the end besides the type and id for the link
  • QueryParams-- how are they meant to be used in conjunction with the models that we produce? It's sort of the same question as above and it goes to this part of the spec here. This one isn't exactly something that I think I'll ever need (most api consumers just grab everything anyways) but some clarification or some tests would help

On a similar vein to question 3, consider the following hierarchy (not entirely valid code)

use models::spam::Spam;

struct Bar {
    id: String,
    spams: Option<Vec<Spam>>
}

jsonapi_model!(Bar; 'bars'; has many spams);

struct Foo {
    id: String,
    bars: Option<Vec<Bar>>;
}

jsonapi_model!(Bar; 'foos'; has many bars);

let bars = vec![Bar::new(), Bar::new(), Bar::new()];
let foo = Foo::new(&bars)

foo.to_jsonapi_document()

This produces the following output (attributes sections omitted since we don't have any):

{
    "data": {
        "type": "foos",
        "id": "1234",
        "relationships": {
            "bars": {
                "data": [
                    { "type": "bars", "id": "31" },
                    { "type": "bars", "id": "32" }
                    { "type": "bars", "id": "33" }
                ]
            }
        }
    },
    "included": [        
        {
            "type": "bars",
            "id": "31",
            "relationships": {
                "spams": {
                    "data": []
                }
            }
        }
        ....rest of the bars
    ]
}

But this makes it seem like I don't have any spams at all, which is totally valid to the perspective of this library because I have't provided that information. This could also be a part of the standard itself, since I've only read through a few of the relevant sections of the spec, but it doesn't seem like included attributes have to provide relationship information. However, even given that limitation, this lib rightfully adds my nested information into the included section of the document. Is it possible to omit the relationship field on nested attributes?

Example JSON doc of what I mean:

{
    "data": {
        "type": "foos",
        "id": "1234",
        "relationships": {
            "bars": {
                "data": [
                    { "type": "bars", "id": "31" },
                    { "type": "bars", "id": "32" }
                    { "type": "bars", "id": "33" }
                ]
            }
        }
    },
    "included": [        
        {
            "type": "bars",
            "id": "31",
            "relationships": {
                "spams": {
                    "data": [
                        { "type": "spams", "id": "uuid-84848" }
                    ]
                }
            }
        }
        ....rest of the bars
        {
            // This shouldn't exist
            "type": "spams",
            "id": "uuid-84848",
            "attributes": {
                ...attrs
            }
        }
    ]
}

Again I'd love to help out with figuring this out since this lib seems very focused and very well made!

Edit: I think part of the last point could be fixed if serde's skip_if directive was taken into account for the relationship fields? I can skip serializing the main attributes but not any relationship fields, they just default to an empty slice.

JsonApiDocument must not have data and errors field?

Hi, thanks for creating a nice crate!

I noticed that when I create a json formatted string like following code, it includes errors with a null value which must not included with data on the top level.
The code is a part of a server side JSON API which returns a data successfully.

let a = JsonApiDocument {
  data: Some(PrimaryData::Multiple(resources)),
  errors: None,
  meta: None,
  included: None,
  links: None,
  jsonapi: None,
}

let string = serde_json::to_sring(&a).unwrap();

According to the specification of JSONAPI, it says:

The members data and errors MUST NOT coexist in the same document.

http://jsonapi.org/format/#document-top-level

and also, its json.schema is defined to not allow to have errors for success documents.
I thought that it's probably ok to have errors with null but it looks no.

Here's my quick fix to remove errors and data keys if it's a None.
But I think that the code can be more suitable by making JsonApiDocument into an enum and divide it according in three, success, failure and info.

Add integration tests

The crate should be tested against an integration-test-compatible local web server which provides the JSON API. This could be done by having a tests/common module, which would be responsible for spawning instances of a local web server (returning their port to the test unit), similar to this, and then have every integration test send requests to their unique instance.

How to add relationships to a model

I'm in need of creating new resources on an endpoint from data structures in Rust, and I'm having some difficulty figuring out how to add related resources to the model when I send it to the server. How would I go from a data structure, to serializing it with required relationships (ie: foreign keys)?

Should included arrays be de-duplicated for same id,type?

Hi, for now ,the following example will generate 4 chapters with id =1 :

    let book = vec![
        Book {
            id: "1".into(),
            title: "The Fellowship of the Ring".into(),
            first_chapter: Chapter {
                id: "1".into(),
                title: "A Long-expected Party".into(),
                ordering: 1,
            },
            chapters: vec![Chapter {
                id: "1".into(),
                title: "A Long-expected Party".into(),
                ordering: 1,
            }],
        },
        Book {
            id: "2".into(),
            title: "The Fellowship of the Ring".into(),
            first_chapter: Chapter {
                id: "1".into(),
                title: "A Long-expected Party".into(),
                ordering: 1,
            },
            chapters: vec![Chapter {
                id: "1".into(),
                title: "A Long-expected Party".into(),
                ordering: 1,
            }],
        },
    ];

    let doc = vec_to_jsonapi_document(book);
    dbg!(&doc);
    assert!(doc.is_valid());
 included: Some(
            [
                Resource {
                    _type: "chapters",
                    id: "1",
                    attributes: {
                        "ordering": Number(
                            1,
                        ),
                        "title": String(
                            "A Long-expected Party",
                        ),
                    },
                    relationships: None,
                    links: None,
                    meta: None,
                },
                Resource {
                    _type: "chapters",
                    id: "1",
                    attributes: {
                        "ordering": Number(
                            1,
                        ),
                        "title": String(
                            "A Long-expected Party",
                        ),
                    },
                    relationships: None,
                    links: None,
                    meta: None,
                },
                Resource {
                    _type: "chapters",
                    id: "1",
                    attributes: {
                        "ordering": Number(
                            1,
                        ),
                        "title": String(
                            "A Long-expected Party",
                        ),
                    },
                    relationships: None,
                    links: None,
                    meta: None,
                },
                Resource {
                    _type: "chapters",
                    id: "1",
                    attributes: {
                        "title": String(
                            "A Long-expected Party",
                        ),
                        "ordering": Number(
                            1,
                        ),
                    },
                    relationships: None,
                    links: None,
                    meta: None,
                },
            ],

Should these chapters be deduplicated?

problems with related resources example

I'm having issues getting an example of related resources working. You can find my example here:

Branch: https://github.com/ttdonovan/jsonapi-rust/tree/ttdonovan/example-related-resources

Diff: master...ttdonovan:ttdonovan/example-related-resources

According to https://jsonapi-validator.herokuapp.com/ the data/related_resources.json is a valid and compliant.

I'm wondering if the issue with the "relationships" and having the correct models defined in Rust. If that is the case then should anything not defined just be ignored like an "attributes"?

Any insight or guidance would greatly be appreciated.

Resources with optional `id`

Thanks for providing this crate, I've found it really useful as a base for my jsonapi implementation.

One problem that I've run into is that the jsonapi spec allows id to be optional for create operations where an id will be generated. This library requires that all Resource objects have an id and they fail to serialize from any json resource representation that doesn't include an id field.

This might be achievable by expanding the PrimaryData definition to add an option which takes some kind of PartialResource or something. The id field of the Resource struct could be make optional, but that would violate the jsonapi spec in every case except this specific one.

I'm not sure what the right answer is, but I would appreciate it if this use case can be supported. For now I'm using dummy id values in my create requests as a workaround, but it doesn't match the behavior of other APIs we have that are implemented in other languages.

Nested relations' relations become attributes instead of relations

Instead of creating something like this:

"data": {
    "type": "inventory-purchase-order",
    "attributes": {
        "purchase-order-lines": [
            {
                "parts": [],
                "price": "99.99",
                "purchase-order": null,
                "purchased-quantity": 1,
                "sku": {
                    "assembly-configurations": [],
                    "description": null,
                    "id": "21",
                    "kind": "case",
                    "manufacturer": null,
                    "reorder-quantity": 0,
                    "serialized": false,
                    "sku": "RCV-TEST-0001"
                }
            }
        ],
    },
    "relationships": {
        "purchase_order_lines": {
            "data": [
                {
                    "type": "inventory-purchase-order-line",
                }
            ]
        }
    }
},

It should create this:

"data": {
    "type": "inventory-purchase-order",
    "relationships": {
        "purchase-order-lines": {
            "data": [
                {
                    "meta": {
                        "attributes": {
                            "price": "99.99",
                            "sku-id": "21",
                        }
                    }
                }
            ]
        }
    }
}

usage for `from_jsonapi_document` to collection of models

I'm a beginner with rust, and apologize if this question is really basic:

What does the usage look like for converting a JsonApiDocument with an array of results to a vector of jsonapi_model structs.

It looks like I need to handle this case https://github.com/michiel/jsonapi-rust/blob/master/src/model.rs#L38 but when it hits https://github.com/michiel/jsonapi-rust/blob/master/src/model.rs#L182, the return type is the singular jsonapi_model and not a vector of them (I think).

thanks in advance

Enable optional has many relations

This can be achieved using a trait:

/// Trait which allows a `has many` relationship to be optional.
pub trait JsonApiArray<M> {
    fn get_models(&self) -> &[M];
    fn get_models_mut(&mut self) -> &mut [M];
}

impl<M: JsonApiModel> JsonApiArray<M> for Vec<M> {
    fn get_models(&self) -> &[M] { self }
    fn get_models_mut(&mut self) -> &mut [M] { self }
}

impl<M: JsonApiModel> JsonApiArray<M> for Option<Vec<M>> {
    fn get_models(&self) -> &[M] {
        self.as_ref()
            .map(|v| v.as_slice())
            .unwrap_or(&[][..])
    }

    fn get_models_mut(&mut self) -> &mut [M] {
        self.as_mut()
            .map(|v| v.as_mut_slice())
            .unwrap_or(&mut [][..])
    }
}

And then changing the macro to generate:

fn build_relationships(&self) -> Option<Relationships> {
    let mut relationships = HashMap::new();
    $(
        relationships.insert(stringify!($has_one).into(),
            Self::build_has_one(&self.$has_one)
        );
    )*
    $(
        relationships.insert(
            stringify!($has_many).into(),
            {
                let values = &self.$has_many.get_models();
                Self::build_has_many(values)
            }
        );
    )*
    Some(relationships)
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.