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

fix: Allows valid non-object JSON to be retrieved in simple-json columns #6574

Merged
merged 16 commits into from Oct 10, 2020
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
48 changes: 31 additions & 17 deletions docs/entities.md
Expand Up @@ -2,21 +2,29 @@

# Entities

* [What is Entity?](#what-is-entity)
* [Entity columns](#entity-columns)
* [Primary columns](#primary-columns)
* [Special columns](#special-columns)
* [Spatial columns](#spatial-columns)
* [Column types](#column-types)
* [Column types for `mysql` / `mariadb`](#column-types-for-mysql--mariadb)
* [Column types for `postgres` / `cockroachdb`](#column-types-for-postgres)
* [Column types for `sqlite` / `cordova` / `react-native` / `expo`](#column-types-for-sqlite--cordova--react-native--expo)
* [Column types for `mssql`](#column-types-for-mssql)
* [`enum` column type](#enum-column-type)
* [`simple-array` column type](#simple-array-column-type)
* [`simple-json` column type](#simple-json-column-type)
* [Columns with generated values](#columns-with-generated-values)
* [Column options](#column-options)
- [Entities](#entities)
- [What is Entity?](#what-is-entity)
- [Entity columns](#entity-columns)
- [Primary columns](#primary-columns)
- [Special columns](#special-columns)
- [Spatial columns](#spatial-columns)
- [Column types](#column-types)
- [Column types for `mysql` / `mariadb`](#column-types-for-mysql--mariadb)
- [Column types for `postgres`](#column-types-for-postgres)
- [Column types for `cockroachdb`](#column-types-for-cockroachdb)
- [Column types for `sqlite` / `cordova` / `react-native` / `expo`](#column-types-for-sqlite--cordova--react-native--expo)
- [Column types for `mssql`](#column-types-for-mssql)
- [Column types for `oracle`](#column-types-for-oracle)
- [`enum` column type](#enum-column-type)
- [`set` column type](#set-column-type)
- [`simple-array` column type](#simple-array-column-type)
- [`simple-json` column type](#simple-json-column-type)
- [Columns with generated values](#columns-with-generated-values)
- [Column options](#column-options)
- [Entity inheritance](#entity-inheritance)
- [Tree entities](#tree-entities)
- [Adjacency list](#adjacency-list)
- [Closure table](#closure-table)

## What is Entity?

Expand Down Expand Up @@ -196,13 +204,13 @@ There are several special column types with additional functionality available:
* `@CreateDateColumn` is a special column that is automatically set to the entity's insertion date.
You don't need to set this column - it will be automatically set.

* `@UpdateDateColumn` is a special column that is automatically set to the entity's update time
* `@UpdateDateColumn` is a special column that is automatically set to the entity's update time
each time you call `save` of entity manager or repository.
You don't need to set this column - it will be automatically set.

* `@DeleteDateColumn` is a special column that is automatically set to the entity's delete time each time you call soft-delete of entity manager or repository. You don't need to set this column - it will be automatically set. If the @DeleteDateColumn is set, the default scope will be "non-deleted".

* `@VersionColumn` is a special column that is automatically set to the version of the entity (incremental number)
* `@VersionColumn` is a special column that is automatically set to the version of the entity (incremental number)
each time you call `save` of entity manager or repository.
You don't need to set this column - it will be automatically set.

Expand Down Expand Up @@ -504,6 +512,12 @@ user.profile = { name: "John", nickname: "Malkovich" };
Will be stored in a single database column as `{"name":"John","nickname":"Malkovich"}` value.
When you'll load data from the database, you will have your object/array/primitive back via JSON.parse

When assigning `"null"` (a string) to a `simple-json` type property, it will be treated as a string and stored in its
imnotjames marked this conversation as resolved.
Show resolved Hide resolved
string format in the database. That means that it will be stored as the string `"null"` not `NULL` in the database.
However, using the javascript primitive `null` will persist a `NULL` in the database.
To avoid storing `"null"` strings in the database, convert your data to a javascript object/array/primitive prior to
assigning it to your entity.

### Columns with generated values

You can create column with generated value using `@Generated` decorator. For example:
Expand Down
9 changes: 2 additions & 7 deletions src/util/DateUtils.ts
@@ -1,4 +1,4 @@
import {ColumnMetadata} from "../metadata/ColumnMetadata";
import { ColumnMetadata } from "../metadata/ColumnMetadata";

/**
* Provides utilities to transform hydrated and persisted data.
Expand Down Expand Up @@ -175,12 +175,7 @@ export class DateUtils {
}

static stringToSimpleJson(value: any) {
try {
const simpleJSON = JSON.parse(value);
return (typeof simpleJSON === "object") ? simpleJSON : {};
} catch (err) {
return {};
}
return typeof value === "string" ? JSON.parse(value) : value;
}

static simpleEnumToString(value: any) {
Expand Down
43 changes: 0 additions & 43 deletions test/github-issues/4440/issue-4440.ts

This file was deleted.

Expand Up @@ -12,5 +12,4 @@ export class Post {
nullable: true
})
jsonField: any;

}
80 changes: 80 additions & 0 deletions test/github-issues/5501/issue-5501.ts
@@ -0,0 +1,80 @@
import "reflect-metadata";
import { Connection } from "../../../src/connection/Connection";
import { closeTestingConnections, createTestingConnections, reloadTestingDatabases } from "../../utils/test-utils";
import { Post } from "./entity/Post";
import { expect } from "chai";

describe("github issues > #5501 Incorrect data loading from JSON string for column type 'simple-json'", () => {

let connections: Connection[];
before(async () => {
connections = await createTestingConnections({
entities: [Post],
schemaCreate: true,
dropSchema: true
});
});
beforeEach(() => reloadTestingDatabases(connections));
after(() => closeTestingConnections(connections));

it("should correctly add/retrieve simple-json field with a string value", () =>
Promise.all(connections.map(async (connection) => {
const repo = connection.getRepository(Post);
const post = new Post();
post.id = 1;
post.jsonField = "";
await repo.save(post);
const postFound = await repo.findOne(1);
postFound!.id.should.eql(1);
postFound!.jsonField.should.eql("");
})));

it("should correctly add/retrieve simple-json field with some object value", () =>
Promise.all(connections.map(async (connection) => {
const repo = connection.getRepository(Post);
const post = new Post();
post.id = 1;
post.jsonField = {"key": "value"};
await repo.save(post);
const postFound = await repo.findOne(1);
postFound!.id.should.eql(1);
postFound!.jsonField.should.eql({"key": "value"});
})));

it("should correctly add/retrieve simple-json field with an array value", () =>
Promise.all(connections.map(async (connection) => {
const repo = connection.getRepository(Post);
const post = new Post();
post.id = 1;
post.jsonField = [{"key": "value"}];
await repo.save(post);
const postFound = await repo.findOne(1);
postFound!.id.should.eql(1);
postFound!.jsonField.should.eql([{"key": "value"}]);
})));

it("should correctly add/retrieve simple-json field with null value", () =>
Promise.all(connections.map(async (connection) => {
const repo = connection.getRepository(Post);
const post = new Post();
post.id = 1;
post.jsonField = null;
await repo.save(post);
const postFound = await repo.findOne(1);
postFound!.id.should.eql(1);
expect(postFound!.jsonField).to.eql(null);
})));

it.only("should correctly add/retrieve simple-json field with 'null' string value", () =>
imnotjames marked this conversation as resolved.
Show resolved Hide resolved
Promise.all(connections.map(async (connection) => {
const repo = connection.getRepository(Post);
const post = new Post();
post.id = 1;
post.jsonField = "null";
await repo.save(post);
const postFound = await repo.findOne(1);
postFound!.id.should.eql(1);
expect(postFound!.jsonField).to.eql("null");
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This expectation is now specifically noted in the docs for simple-json.

})));

});