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 all 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
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;

}
112 changes: 112 additions & 0 deletions test/github-issues/5501/issue-5501.ts
@@ -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;
})));
});