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

Nested schemas in Array type cause properties not to be written to ddb #196

Closed
estyh opened this issue Nov 15, 2021 · 5 comments
Closed
Labels
enhancement New feature or request

Comments

@estyh
Copy link

estyh commented Nov 15, 2021

Describe the bug
If you use nested schemas for fields typed as Array, Model.create method will not write the parent field.
Possibly related - typescript doesn't know anything about the nested schema and types the parent field as any[]. This happens with nested schema for Obect types as well - you get a ts error if you try to access a nested field: Property 'fieldName' does not exist on type 'object'

To Reproduce

/*
    debug.ts - Just for debug

    Edit your test case here and invoke via: "jest debug"

    Or run VS Code in the top level directory and just run.
 */
import {
	AWS,
	Client,
	Entity,
	Match,
	Model,
	Table,
	print,
	dump,
	delay,
} from './utils/init';

jest.setTimeout(7200 * 1000);

//  Change with your schema
const schema = {
	version: '0.0.1',
	indexes: {
		primary: { hash: 'pk', sort: 'sk' },
		gs1: { hash: 'gs1pk', sort: 'gs1sk', project: 'all' },
	},
	models: {
		User: {
			pk: { type: String, value: '${_type}#' },
			sk: { type: String, value: '${_type}#${id}' },

			gs1pk: { type: String, value: '${_type}#' },
			gs1sk: { type: String, value: '${_type}#${id}' },

			name: { type: String },
			email: { type: String },
			id: { type: String, uuid: 'uuid' },
			addresses: {
				type: Array,
				required: true,
				schema: {
					line1: { type: String, required: true },
					line2: { type: String },
					city: { type: String },
					state: { type: String },
					zip: { type: String },
				},
			},
		},
	},
};
type UserType = Entity<typeof schema.models.User>;
//  Change your table params as required
const table = new Table({
	name: 'DebugTable',
	client: Client,
	schema,
	logger: true,
});

//  This will create a local table
test('Create Table', async () => {
	if (!(await table.exists())) {
		await table.createTable();
		expect(await table.exists()).toBe(true);
	}
});

test('Test', async () => {
	// let User = table.getModel('User');
	// let users = await User.find({});
	let User = table.getModel<UserType>('User');
	const user = await User.create({
		email: '[email protected]',
		name: 'User',
		addresses: [
			{
				line1: '123 Main St',
				city: 'Anywhere',
				state: 'CA',
				zip: '12345',
			},
		],
	});
	let foundUser = await User.get({ id: user.id });
	console.log(foundUser);
	expect(foundUser.addresses).toStrictEqual([
		{
			line1: '123 Main St',
			city: 'Anywhere',
			state: 'CA',
			zip: '12345',
		},
	]);
});

test('Destroy Table', async () => {
	await table.deleteTable('DeleteTableForever');
	expect(await table.exists()).toBe(false);
});

{
        "trace": {
            "model": "User",
            "cmd": {
                "ConditionExpression": "(attribute_not_exists(pk)) and (attribute_not_exists(sk))",
                "TableName": "DebugTable",
                "Item": {
                    "pk": {
                        "S": "User#"
                    },
                    "sk": {
                        "S": "User#fceda3ad-e976-4e63-ba8c-5ab6c6fdcd5b"
                    },
                    "gs1pk": {
                        "S": "User#"
                    },
                    "gs1sk": {
                        "S": "User#fceda3ad-e976-4e63-ba8c-5ab6c6fdcd5b"
                    },
                    "name": {
                        "S": "User"
                    },
                    "email": {
                        "S": "[email protected]"
                    },
                    "id": {
                        "S": "fceda3ad-e976-4e63-ba8c-5ab6c6fdcd5b"
                    },
                    "_type": {
                        "S": "User"
                    }
                },
                "ReturnValues": "NONE"
            },
            "op": "put",
            "properties": {
                "pk": "User#",
                "sk": "User#fceda3ad-e976-4e63-ba8c-5ab6c6fdcd5b",
                "gs1pk": "User#",
                "gs1sk": "User#fceda3ad-e976-4e63-ba8c-5ab6c6fdcd5b",
                "name": "User",
                "email": "[email protected]",
                "id": "fceda3ad-e976-4e63-ba8c-5ab6c6fdcd5b",
                "_type": "User"
            }
        }
    }

Expected behavior

  1. The nested Array field (addresses) should have been assigned to the new user

Screenshots
If applicable, add screenshots to help explain your problem.

Environment (please complete the following information):

  • OS Windows 10
  • Node Version 14.15.3
  • OneTable Version 2.1.0

Additional context
I'm having a hard time in general using typescript with dynamodb-onetable. As mentioned above, you don't get typings for nested schemas, and in fact, typescript will complain if you try to access a nested field. Even more problematic is the issue already mentioned in #185
Thank you

@mobsense
Copy link
Contributor

Thanks. We'll check this out.

But for immediate feedback, the nested schema must be an Object not Array. i.e. addresses cannot be an array and use nested schemas.

We'd really like nested schemas to work for TS on Arrays and Objects to any depth, but currently it only works on Objects. AND, you do not get TS bindings for the nested schema.

We're exploring the TS magic required to make this work, but TS typings are a dark art and we are still looking for the magic potion to make it happen. We want nested schemas to be automatically generated from the schema and not require a build-time generate to create typings.

@mobsense mobsense added the enhancement New feature or request label Nov 16, 2021
@oliverbutler
Copy link

@mobsense Is there an issue with wanting build-time generated types? Prisma does this and it seems like a nice way around TS black magic

@mobsense
Copy link
Contributor

Using TS magic, you get full typing of your schema and data in tools like VS code.

Requiring a build step would make this impossible without building after each code change.

@melissarh57
Copy link
Contributor

Second this bug fix! Right now I'm getting the same error that the property doesn't exist when using a nested object schema

@dev-embedthis
Copy link
Contributor

Using the following should work.

i.e. Field.items with a nested schema.

            addresses: {
                type: Array,
                required: true,
                items: {
                    type: Object,
                    schema: {
                        line1: {type: String, required: true},
                        line2: {type: String},
                        city: {type: String},
                        state: {type: String},
                        zip: {type: String},
                    },
                },
            },

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

5 participants