Git Product home page Git Product logo

jsonapi-mapper's Introduction

JSON API Mapper

Build Status npm version david dm

JSON API Mapper (formerly Oh My JSON API) is a wrapper around @Seyz's excellent JSON API-compliant serializer, jsonapi-serializer, that removes the pain of generating the necessary options needed to serialize each of your ORM models.

Join the chat at https://gitter.im/scoutforpets/oh-my-jsonapi

Breaking Changes

This project has recently been renamed and rewritten using Typescript. While most functionality is generally the same, there have been a few deprecations and changes to how the Mapper is initialized. Please see the migration guide below.

How does it work?

A serializer requires some sort of 'template' to understand how to convert what you're passing in to whatever you want to come out. When you're dealing with an ORM, such as Bookshelf, it would be a real pain to have to generate the 'template' for every one of your Bookshelf models in order to convert them to JSON API. JSON API Mapper handles this by dynamically analyzing your models and automatically generating the necessary 'template' to pass to the serializer.

What ORMs do you support?

Initially, we only provide a mapper for Bookshelf. However, the library can be easily extended with new mappers to support other ORMs. PR's are more than welcome!

How do I install it?

npm install jsonapi-mapper --save

How do I use it?

It's pretty simple. You only need to configure the mapper and then use it as many times you need.

import Mapper = require('jsonapi-mapper');

// Create the mapper
var mapper = new Mapper.Bookshelf('https://api.hotapp.com');

// Use the mapper to output JSON API-compliant using your ORM-provided data
return mapper.map(myData, 'appointment');

How can I contribute?

God bless you if you're reading this! Check out our Contributing page

Migrating from OhMyJSONAPI

The migration process is painless:

  • Remove oh-my-jsonapi from your project.

  • Run npm install jsonapi-mapper --save

  • Convert any instances of the constructor:

    new OhMyJSONAPI('bookshelf', 'https://api.hotapp.com');

    to

    new Mapper.Bookshelf('https://api.hotapp.com');
  • Convert any instances of:

    jsonApi.toJSONAPI(myData, 'appointment');

    to

    mapper.map(myData, 'appointment');

API

new Mapper.Bookshelf(baseUrl, serializerOptions)
  • (optional) baseUrl (string): the base URL to be used in all links objects returned.
  • (optional) serializerOptions (string): options to be passed to the serializer. These options will override any options generated by the mapper. For more information on the raw serializer, please see the documentation here.
mapper#map(data, type, mapperOptions)
  • data (object): The data object from Bookshelf (either a Model or a Collection) to be serialized.
  • type (string): The type of the resource being returned. For example, if you passed in an Appointment model, your type might be appointment.
  • (optional) mapperOptions (object):
    • (optional) attributes (object):
      • (optional) omit (RegExp | string)[]: List of model attributes to omit from the resulting payload. For example, you may wish to exclude any foreign keys (as recommended by the JSON API-spec). Note: the model's idAttribute is automatically excluded by default.
      • (optional) include (RegExp | string)[]: List of model attributes to explicitly include from the resulting payload.
    • (optional) keyForAttr (function (string => string)): Function to customize the attributes keys. The function is passed as input the attribute key (string) and output the new attribute key (string).
    • (optional) relations (boolean | object): Flag to enable (true) or disable (false) serializing of related models on the response. Alternatively, you can provide an object containing the following options:
      • included (boolean | string[]) (default: true) - includes data for all relations in the response. You may optionally specify an array containing the names of specific relations to be included.
      • fields string[] - an array of relation names that should be included in the response.
    • (optional) typeForModel (object | function): To customize the type of a relation. If the value returned is falsy, then it's automatically pluralized. This function also affects the type passed as the second parameter of the map function.
      • object: The object should have the structure {"<relation-name>": "<type-to-use>"} (e.g. {"best-friend": "people"}).
      • function (string => string): The function is passed as input the relation name (string) and output the type for that relation (string) (e.g. (x) => x + '-resources').
    • (optional) enableLinks (boolean): Flag to enable (true) or disable (false) the generation of links in the payload. This may be useful if the consuming system doesn't take advantage of links and you want to save on payload size and maybe a bit of performance. Defaults to true.
    • (optional) query (object): An object containing the original query parameters. These will be appended to self and pagination links. Developer Note: This is not fully implemented yet, but following releases will fix that.
    • (optional) pagination (object): Pagination-related parameters for building pagination links for collections.
      • (required) offset (integer)
      • (required) limit (integer)
      • (optional) total or rowCount (integer)
    • (optional) meta (object): Top-level meta object to include in the document
    • (optional) outputVirtuals (boolean): Flag to include/omit the virtuals if the virtuals plugin is used, overrides the default outputVirtuals value.

How can I contribute?

The project is very open to collaboration from the public, especially on providing the groundwork for other ORM's (like Sequelize or Mongoose). Just open a PR!

The project source has been recently rewritten using Typescript, which has been proven useful for static checks and overall development.

Credits

  • Thanks to @Seyz. Without his work, the project would not be possible.
  • Thanks to the zomoz team (especially @ShadowManu) for their hard work in migrating the codebase to Typescript and writing a comprehensive test suite.

jsonapi-mapper's People

Contributors

chamini2 avatar confuser avatar jamesdixon avatar jcao02 avatar mp-ffx avatar npatmaja avatar peterviergutz avatar ralfschimmel avatar shadowmanu 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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

jsonapi-mapper's Issues

stringifying json with circular dependencies

I ran into an issue where data I received from an external API contained circular references, which throws an error when stringifying. Normally, this could be resolved by using something like fast-safe-stringify, but given that we have our own method of converting to JSON, this may be not be an option. I haven't investigated in any depth, but wanted to bring it up.

support for disabling links

According to the spec, links are optional. In practice, I haven't seen much that takes advantage of them. For example, I'm using Ember on the frontend and Ember Data does have some support, but it seems to be extremely limited even though it's listed as one of the gold standards as far as implementations go.

That said, I believe it might be good to offer an option to enable/disable links. Slight performance boost and definitely some savings in terms of payload size.

@ShadowManu @jcao02 thoughts? Are links something you're taking advantage of?

Mapper won't work with custom idAttributes

When specifying a custom idAttribute in bookshelf model, the serialization crashes setting the id as undefined. This is because the id used by the mapper is inside the model.attributes object, which isn't right since the real id is set in model.id

Missing related object in included when foreign key in the last row is null

Hi, I have a problem when mapping a collection that has a non mandatory belongsTo relationship. So when the last row's foreign key of the fetched data is null, then mapper wont include all that related object into included even though there are some other rows that have that foreign key filled. To better picture the case consider the following table:

table: foo

id name bar_id
1 name1 1
2 name2 <null>
3 name3 <null>
4 name4 2

Bookshelf model:

const Foo = bookshelf.Model.extend({
  tablename: 'foo',

  bar() {
    return this.belongsTo('Bar', 'bar_id');
  },
});

When I fetched the first 3 rows, the Bar object won't be included in the resulting object after being mapped. However, when I fetched all the rows, the last row's bar_id is present, the Bar object is correctly included.

Please let me know if I'm missing something in my implementation.

I'm using [email protected] and [email protected]

Fix mapRelations nested links and specs

The PR #42 added a recursive function mapRelations to handle nested relations, but is not correctly tested and also behaves incorrectly for the generated links in the nested relations; we need to either fix the links for nested relations or remove them.

Also, this functions needs to be tested, since the specs currently don't cover it thoroughly.

Map a model with deep (two levels) of has-many nested relationship

Hi, I have a model that has a has many relationship that also has a has many relationship

const A = bookshelf.Model.extend({
  b() {
    return this.hasMany(B);
  }
});

const B = bookshelf.Model.extend({
  c() {
    return this.hasMany(C);
  }
});

const C = bookshelf.Model.extend({});

// Queries
A.forge().fetch({ withRelated: ['b.c'] });

When I map the resulting model with jsonapi-mapper, it returns only the first level of the relationship (A has many B). Is this functionality not supported yet?

note:
After looking at the source code, it seems that the function mapRelations works only for model as type Collection does not have property relations (it has property models tho), please do correct me if I'm wrong.

bug in jsonapi serializer

There's an issue in the latest version of the JSON API serializer that is causing empty relations to be returned. I've submitted a PR, but there hasn't been any movement on the repo in quite some time. We may want to consider switching to my fork until the issue is resolved in the parent repo.

Relation names are used for model types.

First of all; great work ๐Ÿ‘

However; I noticed that relation names are used as model types.
For example; I have a model with two belongsTo's called goodGuy and badGuy of the type guy. The mapper returns JSON with related data as expected, though the type of the models is goodGuys and badGuys instead of guys for both.

Missing _id properties

I have an attribute named player_id. In this particular instance, it shouldn't have a relationship and should simply be output into the attributes property.

The response from the mapper doesn't contain it.

It looks like anything that ends with _id is being explicitly left out https://github.com/scoutforpets/jsonapi-mapper/blob/v0.9.0/src/bookshelf/utils.ts#L49

Is there a way to disable this behaviour, or at least mark fields to ignore? Current work around is to map the data, and reattach the missing attribute manually.

Looks like this came up in #23

Bad template generation for related collections

There's a problem in the getSample function (here) where you're attempting to merge multiple Bookshelf objects using lodash merge function.

More specifically: If you try to merge two objects with a common key containing a Bookshelf Collection, for some reason lodash merge does not recur in the collections but it compares them as a whole keeping the last one as the final value. This can result in a template error if the last collection is empty or if it contains only models that lack any of the attributes you wish to serialize.

If you need to reproduce the error:

let obj = bookshelf.Model.forge({ a: 1 });
let objs = bookshelf.Collection.forge([obj]);
let objs2 = bookshelf.Collection.forge([]);

let x = [
  {
    relations: {
      relName: objs
    }
  },
  {
    relations: {
      relName: objs2
    }
  }
];

console.log(JSON.stringify(_.reduce(x, _.merge, {})));

`typeForAttribute` option overriding causing `pluralizeType` not to have an effect

@ShadowManu @chamini2 I just upgraded to the beta and am running into a regression where it seems that the options I'm passing directly to the serializer are no longer working.

Specifically, I pass in the pluralizeType and keyForAttribute options. After some investigation, it appears that the addition of the relationTypes attribute automatically creates a typeForAttribute option which is passed to the serializer.

I'm not sure I see the point of the relationTypes option when this functionality could already be had by just passing in typeForAttribute as a serializer option.

Thoughts?

Mapper removes attributes that end in `type` and `id`

I understand this is intended behaviour, but shouldn't there be an option to pass attributes to not be omitted from the output?

Another property in the mapperOptions param like { keepAttributes: ['keep_type', 'thing-id'] }

Virtual properties are not mapped

When sending in a bookshelf model to map where there are virtual properties added (using the Virtuals plugin) the mapper ignores them. I have a fix for util.ts that will check for the presence of virtual properties and when the virtual properties are marked visible they will get added to the attributes that will be part of the JSONAPI payload returned.

relations currently aren't returned in links if relations aren't returned in the model

This is an issue that stems from Bookshelf not returning relations if withRelated isn't specified on the query. It can be worked around by manually specifying the relations on the model, but unfortunately, there is an issue with the JSON API serializer that won't return a null data element for relations that have no attributes. Opened issues in both repositories.

Project Development

Hi. Before all, nice project going on here. My team requires a Bookshelf models -> json api responses, mapping tool. I liked this project (and the serializer project behind) and plant to use it as a base.

However, changes like using Typescript and more testing (you know 4 tests is not enough) are features we want to set on the tool. What's your view on this? Do you prefer it to simply be a fork or are more interested on integration to the project?

1.0 API

Synopsis
Discuss pros, cons and implementation of our own API that will prevent changes in the underlying serializer API from affecting the mapper.

See #57 for further discussion on this topic.

relationships aren't returned according to spec

When a model has a relationship, according to spec, a reference to the relationship should always be returned with the payload. For example, a book model that has a one-to-one relationship with an author, should be returned like so:

{
    "data": {
        "type": "book",
        "id": "1",
        "attributes": {
            "title": "Mistborn"
        },
        "relationships": {
            "author": {
                "links": {
                    "self": "http://example.com/books/1/relationships/author",
                    "related": "http://example.com/books/1/author"
                },
                "data": {
                    "type": "author",
                    "id": "1"
                }
            }
        },
        "links": {
            "self": "https://localhost:3001/books/1"
        }
    }
}

However, the response generated by the library looks like this:

{
    "data": {
        "type": "book",
        "id": "1",
        "attributes": {
            "title": "Mistborn",
                        "authorId": 1
        },
        "links": {
            "self": "https://localhost:3001/books/1"
        }
    }
}

There are two problems:

  1. Relationships are not returned unless included (this is the same as/related to #13)
  2. The foreign key (staffId) is being returned and it shouldn't be. According to the spec, or at least how I've interpreted it, the underlying relation semantics should be hidden and only exposed with the relationships key.

As far as I can tell, the only way to solve issue 1 is to always call withRelated on your Bookshelf models as the spec requires the relationship reference to always be returned. However, in order to construct this reference, both the type of relation and its id are needed. This information isn't accessible in Bookshelf unless the relationship is included, except in the case of a one to _x_ relationship where a relation can be derived from a foreign key on the model.

I believe there are two ways to solve issue 2:

  • The short term solution would be to use some convention logic to parse out any fields referencing an id and then transforming them into a relation.
  • The better solution would be to analyze the the relation (if passed), find any that are one to _x_ and then remove the corresponding keys in the model. However, this relates back to issue 1 mentioned above.

@ShadowManu @jcao02 any thoughts on this?

Link formation should consider cases for string literal with spaces and encode them

I've encountered an issue when the links for a json object are created. The code I'm generating forms a json object with a value of:

{
  "payload" : {
    "data": {
        "id": "test for it",
        "type": "tests"
      }
  }
}

after passing this object through the mapper the expected link to be created should be encoded for urls like this:

{
    "data": {
        "id": "test for it",
    },
    "self": "/tests/test%20for%20it"
}

instead it is being created this way

{
    "data": {
        "id": "test for it"
    },
    "self": "/tests/test for it"
}

Is it the expected behavior ?

options for serializing relationships

Currently, we have a method to completely disable the serialization AND inclusion of relationships via the relations property. However, this option only affects whether or not relations are included in the payload; it's either all or nothing. We have no public API for serialization of the relationships, but not including the actual related resources as part of the payload. This can be done by passing included: false to the serializer directly, but it needs to be passed as a property of the relationship itself. Currently, this doesn't work properly because we pass all serializerOptions at the root level.

This is both a bug and an opportunity to define a public API as to whether or not relationships are included as part of the payload.

My suggestion for the API:

  • relations - this property would remain the same and would strictly affect whether or not relations are serialized as part of the payload, either as linkage or included.
  • includeRelations - this property affects whether or not the relations are included as a compound document (embedded).

Scenarios:

Relationships enabled and included

  • relations: true
  • includeRelations: true
{
  "included": [
    {
      "type": "pet",
      "id": "60",
      "attributes": {
        "name": "Toney"
    }
  ],
  "data": {
    "type": "customer",
    "id": "37",
    "attributes": {
      "firstName": "Peter",
      "lastName": "Fritsch"
    },
    "relationships": {
      "pets": {
        "data": [
          {
            "type": "pet",
            "id": "60"
          }
        ]
      }
    }
  }
}

Relationships enabled and NOT included

  • relations: true
  • includeRelations: false
{
  "data": {
    "type": "customer",
    "id": "37",
    "attributes": {
      "firstName": "Peter",
      "lastName": "Fritsch"
    },
    "relationships": {
      "pets": {
        "data": [
          {
            "type": "pet",
            "id": "60"
          }
        ]
      }
    }
  }
}

Relationships disabled

  • relations: false
  • includeRelations: false (has no effect)
{
  "data": {
    "type": "customer",
    "id": "37",
    "attributes": {
      "firstName": "Peter",
      "lastName": "Fritsch"
    }
  }
}

@chamini2 @ShadowManu thoughts? I'm going to start working on this now as it's something I need to implement, but open to changes.

camelCased foreign keys are returned

Currently, we're only filtering out underscore_case and dash-case id/types. We also need to check for camelCase.

Example:

businessId should not be returned as part of the payload as it's a foreign key.

Changes on attribute fields from kebab case to snake case

Hi, I just updated to beta 7 and I notice that the attributes fields changed from kebab case to snake case, which broke my project. For example my table column is transaction_status and in beta 6 and prior it is mapped to transaction-status but in beta 7 it is mapped to transaction_status. Is this behavior intentional?

bookshelf not creating full schema for relations

When dealing with a collection, the current code only grabs the first model to use as a template. This works fine unless there are relations. If the first model retrieved doesn't have a relation, but others do, adapter will generate a bad schema that will be applied to other items in the collection, resulting in bad output.

relation fields param not limiting as intended

Although the tests pass, the fields param is not properly limiting the relationships that are serialized.

The issue with the tests passing seems to be something related to how _.match works. When testing an array, it appears that the specified array doesn't need to match exactly. Meaning that the array could have other elements and a match would still be true.

I'll submit a PR to fix the issue.

including deeply nested resources doesn't work

When attempting to include a deeply nested resource, only the top-level resource is actually returned. For example, imagine you have an appointment. That appointment has a customer and that customer has one or more pet(s). In order to return that appointment along with the customer and the pet(s), you'd request the following url:

/appointments?include=customer.pets, which would return customer data as well as any related pet(s).

Currently, the addon will only return the top-level relation (customer).

See the spec here: http://jsonapi.org/format/#fetching-includes

self link for included relations is wrong

When including a relation, the self link is using the incorrect entity in the url. For example, a Customer model has a relation with a User model. Calling GET /customers?include=user returns the following:

{
  "links": {
    "self": "https://localhost:3001/customers"
  },
  "included": [
    {
      "type": "user",
      "id": "1",
      "attributes": {
        "firstName": "Dorian",
        "lastName": "Rau",
        "email": "[email protected]",
        "createdAt": "2016-04-27T04:41:42.573Z",
        "updatedAt": "2016-04-27T04:41:42.573Z"
      },
      "links": {
        "self": "https://localhost:3001/customers/1"
      }
    }
  ],
  "data": {
    "type": "customer",
    "id": "1",
    "attributes": {
      "userId": 1,
      "keyCode": null,
      "active": true,
      "archived": false,
      "createdAt": "2016-04-27T04:41:42.658Z",
      "updatedAt": "2016-04-27T04:41:42.658Z"
    },
    "links": {
      "self": "https://localhost:3001/customers/1"
    },
    "relationships": {
      "user": {
        "data": {
          "type": "user",
          "id": "1"
        },
        "links": {
          "self": "https://localhost:3001/customers/1/relationships/user",
          "related": "https://localhost:3001/customers/1/user"
        }
      }
    }
  }
}

Notice that the self link in the included relation is:

"links": {
        "self": "https://localhost:3001/customers/1"
      }

rather than

"links": {
        "self": "https://localhost:3001/users/1"
      }

issues with mapperOptions vs serializerOptions

It seems there are some issues when passing in serializerOptions. I believe this is in part because the actual JSONAPISerializer docs are a bit confusing. For example, if you'd like to return only the ids of your model's relationships, you must pass included: false to the serializer. However, this only works when included in the relationship options and not on a global level. As mentioned earlier, this isn't well documented; I only figured it out first through trial and error and then finally looking at the tests.

Unfortunately, I was unable to get this property passed in properly to the relationship and therefore couldn't get this working with the mapper.

Point is, I think we need to clear up our documentation, submit a PR for the serializer documentation and make any changes to allow serializer options to be passed through properly.

This probably also relates to the discussion regarding #59.

support for relationship links when no data exists

It appears that I may have been mis-understanding/underestimating the value of links provided by the JSON API-spec. I'm using Ember on the front-end can use the related link of a relationship to asynchronously load relationship data when needed. For example, if I have a customer model with a pets relationship and that pets relationship contains ONLY a links attribute, Ember Data will use the related attribute of links to automatically load that data. This allows for much more efficient loading of data, on-demand.

However, with the mapper, we currently require relationship data in order to build said relationships.

My proposal is a method for passing a list of relationships to the mapper. These relationships can than be cross-referenced against the relationships passed along with the Bookshelf model. If there is no relationship in the model, the mapper would simply build the appropriate link. Otherwise, if there is data, the mapper would build out the relationship as normal.

Thoughts on this?

Custom idAttributes becoming undefined on to-many relationships

Hey, I'm currently using this mapper to parse some models and has worked for the charm until I encountered this error that for to-many relationships. Here is my object model sent to the mapper.
The error is that the mapper is passing undefined ids to the relationships and the inclusions

{
  "id" : 3696,
  "attributes" : {
    "title": "Necessitatibus rerum",
    "description": "Accusamus voluptatem libero incidunt nobis ipsa.",
    "min_people": 1,
    "max_people": 6,
    "slug": "necessitatibus-rerum",
    "id": 3696
    },

    "relations" : {
      "tags": {
        "models" : [
          {
            "id" : "exp",
            "attributes" : {
              "name": "Exp",
              "slug": "exp",
              "_pivot_activity_id": 3696,
              "_pivot_slug": "exp"
            }
          },
          {
            "id" : "full",
            "attributes" : {
              "name": "Full",
              "slug": "full",
              "_pivot_activity_id": 3696,
              "_pivot_slug": "full"
            }
          }
        ] 
      } 
    }
}

here is the output of the mapper

"json" : {
  "data": {
    "type": "activities",
    "id": "3696",
    "attributes": {
      "title": "Necessitatibus rerum",
      "description": "Accusamus voluptatem libero incidunt nobis ipsa.",
      "min-people": 1,
      "max-people": 6,
      "slug": "necessitatibus-rerum"
    },
    "links": {
      "self": "http://localhost:3000/activities/3696"
    },
    "relationships": {
      "tags": {
        "data": [
          {
            "type": "tags",
            "id": "undefined"
          },
          {
            "type": "tags",
            "id": "undefined"
          }
        ],
        "links": {
          "self": "http://localhost:3000/activities/3696/relationships/tags",
          "related": "http://localhost:3000/activities/3696/tags"
        }
      }
    }
  },
  "included": [
    {
      "id" : "undefined",
      "attributes" : {
          "name": "Exp",
          "slug": "exp",  
        },
        "type" : "tags",
        "links" : {
          "self": "http://localhost:3000/tags/undefined"
        }
    }
  ]
}

To be more specific here are the model definitions used

var Activity = bookshelf.Model.extend({
  tableName: 'activities',
  hasTimestamps: true,

  // Relationships
  tags : function(){
    return this.belongsToMany('Tag','has_tags','activity_id','slug')
  }

});

var Tag = bookshelf.Model.extend({
  tableName: 'tags',
  idAttribute: 'slug',
  timestamps: true,

  activities: function() {
    return this.belongsToMany('Activity');
  }
});

Any help with this matter would be greatly appreciated.
Thxs

nested relations only work when the nested relationship is a one-to

Discovered an issue that when trying to access a nested relationship, such as project.tasks where tasks is a many relation, the result is not properly mapped. It only works for something like project.user where user is a one-to relation.

Looking at the code, the issue is obvious and it's a fairly easy fix. However, analyzing the code further shows there is quite a bit of duplication in the map() method. It's only a slightly different case when the we're dealing with a Collection or a Model. I believe this can be refactored to be a recursive function that should reduce the amount of code, solve the issues with dealing with Collection vs. Model and even support very deep nesting of relations rather than a single level.

@ShadowManu any thoughts?

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.