Skip to content

Commit

Permalink
fix: Allows valid non-object JSON to be retrieved in simple-json colu…
Browse files Browse the repository at this point in the history
…mns (typeorm#6574)

Fixes typeorm#5501
  • Loading branch information
mkornatz authored and Svetlozar committed Jan 12, 2021
1 parent ae71319 commit 9c989c0
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 51 deletions.
9 changes: 2 additions & 7 deletions src/util/DateUtils.ts
Original file line number Diff line number Diff line change
@@ -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.

Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,4 @@ export class Post {
nullable: true
})
jsonField: any;

}
112 changes: 112 additions & 0 deletions test/github-issues/5501/issue-5501.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
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 store simple-json field", () => Promise.all(connections.map(async (connection) => {
let id = 0;
const runTestCase = async (input: any, expected: any, message: string) => {
id++;

await connection.getRepository(Post).save({ id , jsonField: input });

const actual = (
await connection.createQueryBuilder()
.from("Post", "post")
.select("post.jsonField", "json")
.where("post.id = :id", {id})
.getRawOne()
)!.json;

expect(actual).to.be.equal(expected, message);
}

await runTestCase("hello world", "\"hello world\"", "normal string");
await runTestCase("", "\"\"", "empty string");
await runTestCase("null", "\"null\"", "string containing the word null");
await runTestCase( { "key": "value" }, "{\"key\":\"value\"}", "object containing a key and string value");
await runTestCase([ "hello" ], "[\"hello\"]", "array containing a string");
await runTestCase(null, null, "a null object value");
await runTestCase(1, "1", "the real number 1");
await runTestCase(0.3, "0.3", "the number 0.3");
await runTestCase(true, "true", "the boolean value true");
await runTestCase(
[ { hello: "earth", planet: true }, { hello: "moon", planet: false } ],
"[{\"hello\":\"earth\",\"planet\":true},{\"hello\":\"moon\",\"planet\":false}]",
"a complex object example"
);
})));

it("should correctly retrieve simple-json field", () => Promise.all(connections.map(async (connection) => {
let id = 0;
const runTestCase = async (input: string | null, expected: any, message: string) => {
id++;
await connection.createQueryBuilder()
.insert()
.into(Post)
.values({id, jsonField: () => ':field'} as any) // A bit of a hack to get the raw value inserting
.setParameter('field', input)
.execute();

const actual = (
await connection.getRepository(Post).findOne({ where: { id } })
)!.jsonField;

expect(actual).to.be.eql(expected, message);
}

await runTestCase("\"hello world\"", "hello world", "normal string");
await runTestCase("\"\"", "", "empty string");
await runTestCase("\"null\"", "null", "string containing the word null");
await runTestCase("{\"key\":\"value\"}", { "key": "value" }, "object containing a key and string value");
await runTestCase("[\"hello\"]", [ "hello" ], "array containing a string");
await runTestCase(null, null, "a null object value");;
await runTestCase("1", 1, "the real number 1");
await runTestCase("0.3", 0.3, "the number 0.3");
await runTestCase("true", true, "the boolean value true");
await runTestCase(
"[{\"hello\":\"earth\",\"planet\":true},{\"hello\":\"moon\",\"planet\":false}]",
[{"hello":"earth","planet":true},{"hello":"moon","planet":false}],
"a complex object example"
);
})));

it("should throw an error when the data in the database is invalid", () => Promise.all(connections.map(async (connection) => {
const insert = (id: number, value: string | null) =>
connection.createQueryBuilder()
.insert()
.into(Post)
.values({ id, jsonField: () => ':field' } as any) // A bit of a hack to get the raw value inserting
.setParameter('field', value)
.execute()

// This was the likely data within the database in #4440
// This will happen if you've tried to manually insert the data in ways where
// we aren't expecting you to - like switching the column type to a text &
// trying to push a value into it that is an object.
await insert(1, "[object Object]");

const repo = connection.getRepository(Post);

const getJson = async (id: number) =>
(
await repo.findOne({ where: { id } })
)!.jsonField;

await expect(getJson(1)).to.be.rejected;
})));
});

0 comments on commit 9c989c0

Please sign in to comment.