Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New year new library, cleaning up unused stuff #4

Closed
wants to merge 12 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ build:
tsc
mv -f dist/src/* dist/
rm -rf dist/src
cp package.dist.json dist/package.json

test:
npx jest

publish:
cd dist/
npm publish
12 changes: 0 additions & 12 deletions changelog.md

This file was deleted.

32 changes: 32 additions & 0 deletions package.dist.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "@jobilla/entity",
"version": "2.0.0",
"description": "A library to encode and decode JSON into entity classes",
"main": "index.js",
"types": "index.d.ts",
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "https://github.com/jobilla/entity"
},
"keywords": [
"entity",
"json",
"decode",
"encode",
"typescript"
],
"author": {
"name": "Leo Sjöberg",
"email": "[email protected]"
},
"contributors": [
{
"name": "Hakan Aktaş",
"email": "[email protected]"
}
],
"license": "MIT"
}
23 changes: 5 additions & 18 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,27 +1,17 @@
{
"name": "@jobilla/entity",
"version": "1.0.2",
"version": "2.0.0",
"description": "A library to encode and decode JSON into entity classes",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "make build",
"test": "jest"
},
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "https://github.com/jobilla/entity"
},
"keywords": [
"entity",
"json",
"decode",
"encode",
"typescript"
],
"author": {
"name": "Leo Sjöberg",
"email": "[email protected]"
Expand All @@ -35,13 +25,10 @@
"license": "MIT",
"devDependencies": {
"@types/jest": "^28.1.4",
"jest": "^28.1.2",
"jest": "^29.7.0",
"prettier": "^3.2.4",
"reflect-metadata": "^0.1.13",
"ts-jest": "^28.0.5",
"typescript": "^4.6.3"
},
"files": [
"dist"
]
"ts-jest": "^29.1.2",
"typescript": "^5.3.3"
}
}
55 changes: 31 additions & 24 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Decahedron Entity
# Jobilla Entity

This package provides a convenient way to decode JSON retrieved from your API or similar, and turning it into a TypeScript class instance.

Expand All @@ -7,7 +7,21 @@ Each class is self-encoding, which means that it knows how to encode itself. As
## Installation

```
yarn add @jobilla/entity
npm install @jobilla/entity
```

## Prerequisites

This library assumes that you have the following options enabled in your `tsconfig.json`:

```json
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"useDefineForClassFields": true
}
}
```

## Usage
Expand All @@ -17,22 +31,20 @@ The basic usage is very straightforward: make your class extend `Entity`, and us
import { Entity, EntityBuilder } from '@jobilla/entity';

class User extends Entity {
// We instantiate with null to ensure the property exists
// at the time of hydration.
public name: string = null;
public email: string = null;
public name!: string;
public email!: string;
jokedos marked this conversation as resolved.
Show resolved Hide resolved
}

fetch('https://api.service.com/v1/users/1')
.then(response => response.Body.json())
.then(response => response.body.json())
.then(jsonData => EntityBuilder.buildOne<User>(User, jsonData));
```

You can also build an array of entities:

```typescript
fetch('https://api.service.com/v1/users')
.then(response => response.Body.json())
.then(response => response.body.json())
.then(jsonData => EntityBuilder.buildMany<User>(User, jsonData));
```

Expand All @@ -59,11 +71,11 @@ There are two ways to solve this. The first one is to simply override the `fromJ
import { Entity, EntityBuilder } from '@jobilla/entity';

class User extends Entity {
public name: string = null;
public email: string = null;
public address: Address = null;
public name!: string;
public email!: string;
public address!: Address;

public fromJson(jsonData: any): User {
public fromJson(jsonData: PartialProps<User>): User {
super.fromJson(jsonData);

if (jsonData.hasOwnProperty('address')) {
Expand All @@ -79,33 +91,28 @@ However, this is quite verbose. Instead, an `@Type` decorator is provided for ne

```typescript
class User extends Entity {
public name: string = null;
public email: string = null;
public name!: string;
public email!: string;
@Type(Address)
public address: Address = null;
public address!: Address;
}
```

If your JSON data comes in with another key, you may specify that manually with:
```typescript
@Type(Address, 'json_key')
```

Note that by default, the `@Type` decorator will assume your JSON comes in snake case. As such,
```typescript
@Type(Address)
public homeAddress: Address = null;
public homeAddress!: Address;
```
will assume that the json holds the key `home_address`. If that is not the case, it should be manually specified as the second argument to `@Type`.
will assume that the json holds the key `home_address`.

#### Note about `Object`
If your entity has a nested object that is **not** represented by another entity, you can also use `@Type(Object)` to annotate that the object should simply be stored as is.

### Encoding back to JSON

Entity objects can also be encoded back to a plain JavaScript Object, or as a JSON string. You can call `toJson()` on any entity to convert it to a plain JS object.
Entity objects can also be encoded back to a plain JavaScript Object. You can call `.toJson()` on any entity to convert it to a plain JS object.

The method defaults to converting your properties to snake case. To prevent this, you can pass `false` as the first argument to `toJson()`. The method also accepts a second boolean argument that lets you specify if the output should instead be as a JSON string. `toJson(true, true)` is identical to `JSON.stringify(toJson(true))`.
The method converts the keys of your properties to snake case.
jokedos marked this conversation as resolved.
Show resolved Hide resolved

## Contributing

Expand Down
50 changes: 24 additions & 26 deletions spec/CircularDeps/CircularDeps.spec.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,30 @@
import { BlogPost } from './blog';
import Comment from './comment';
import { EntityBuilder } from '../../src/EntityBuilder';
import { BlogPost } from "./blog";
import Comment from "./comment";
import { EntityBuilder } from "../../src";

describe('Entity with circular dependency', () => {
it('decodes circularly depended annotated entity', () => {
const blog = EntityBuilder.buildOne(BlogPost, {
title: 'Decahedron/Entity gets circdep',
body: 'hooray!',
comments: [
{ body: 'Yay!' },
],
});

expect(blog.comments).toBeDefined();
expect(blog.comments[0]).toBeDefined();
expect(blog.comments[0].body).toEqual('Yay!');
describe("Entity with circular dependency", () => {
it("decodes circularly depended annotated entity", () => {
const blog = EntityBuilder.buildOne(BlogPost, {
title: "Decahedron/Entity gets circdep",
body: "hooray!",
comments: [{ body: "Yay!" }],
});

it('decodes circularly depended annotated entity other way around', () => {
const comment = EntityBuilder.buildOne(Comment, {
body: 'hooray!',
blog: {
title: 'Decahedron/Entity gets circdep',
body: 'hooray!',
},
});
expect(blog.comments).toBeDefined();
expect(blog.comments[0]).toBeDefined();
expect(blog.comments[0].body).toEqual("Yay!");
});

expect(comment.blog).toBeDefined();
expect(comment.blog.body).toEqual('hooray!');
it("decodes circularly depended annotated entity other way around", () => {
const comment = EntityBuilder.buildOne(Comment, {
body: "hooray!",
blog: {
title: "Decahedron/Entity gets circdep",
body: "hooray!",
},
});

expect(comment.blog).toBeDefined();
expect(comment.blog.body).toEqual("hooray!");
});
});
14 changes: 7 additions & 7 deletions spec/CircularDeps/blog.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Entity } from '../../src/Entity';
import { Type } from '../../src/support/Type';
import Comment from './comment';
import { Entity } from "../../src";
import { Type } from "../../src";
import Comment from "./comment";

export class BlogPost extends Entity {
public title: string = null;
public body: string = null;
public title!: string;
public body!: string;

@Type(() => require('./comment'))
public comments: Comment[] = [];
@Type(() => Comment)
public comments: Comment[] = [];
}
12 changes: 6 additions & 6 deletions spec/CircularDeps/comment.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Entity } from '../../src/Entity';
import { Type } from '../../src/support/Type';
import { BlogPost } from './blog';
import { Entity } from "../../src";
import { Type } from "../../src";
import { BlogPost } from "./blog";

export default class Comment extends Entity {
public body: string = null;
public body!: string;

@Type(() => require('./blog').BlogPost)
public blog: BlogPost = null;
@Type(() => BlogPost)
public blog!: BlogPost;
}
Loading
Loading