Skip to content

Commit

Permalink
feat(entity/schema): add typeHidden option (dynamodb-toolbox#351)
Browse files Browse the repository at this point in the history
- allow hiding entity name in parsed output using the `typeHidden` entity schema property
  • Loading branch information
Geert authored Nov 10, 2022
1 parent 3959d75 commit 6857873
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 6 deletions.
3 changes: 2 additions & 1 deletion docs/docs/entity/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const MyEntity = new Entity({
| createdAlias | `string` | no | Override default _created_ alias name (default: `created`) |
| modifiedAlias | `string` | no | Override default _modified_ alias name (default: `modified`) |
| typeAlias | `string` | no | Override default _entity type_ alias name (default: `entity`) |
| typeHidden | `boolean` | no | When enabled hides the entity type from the returned parsed data (default: _false_) |
| attributes | `object` | yes | Complex type that specifies the schema for the entity (see below) |
| autoExecute | `boolean` | no | Enables automatic execution of the DocumentClient method (default: _inherited from Table_) |
| autoParse | `boolean` | no | Enables automatic parsing of returned data when `autoExecute` evaluates to `true` (default: _inherited from Table_) |
Expand Down Expand Up @@ -84,7 +85,7 @@ For more control over an attribute's behavior, you can specify an object as the
| prefix | `string` | `string` | A prefix to be added to an attribute when saved to DynamoDB. This prefix will be removed when parsing the data. |
| suffix | `string` | `string` | A suffix to be added to an attribute when saved to DynamoDB. This suffix will be removed when parsing the data. |
| transform | `function` | all | A function that transforms the input before sending to DynamoDB. This accepts two arguments, the value passed and an object containing the data from other attributes. |
| format | `function` | all | A function that transforms the DDB output before sending it to the parser. This accepts two arguments, the value of the attribute and an object containing the whole item. |
| format | `function` | all | A function that transforms the DDB output before sending it to the parser. This accepts two arguments, the value of the attribute and an object containing the whole item. |
| partitionKey | `boolean` or `string` | all | Flags an attribute as the 'partitionKey' for this Entity. If set to `true`, it will be mapped to the Table's `partitionKey`. If set to the name of an **index** defined on the Table, it will be mapped to the secondary index's `partitionKey` |
| sortKey | `boolean` or `string` | all | Flags an attribute as the 'sortKey' for this Entity. If set to `true`, it will be mapped to the Table's `sortKey`. If set to the name of an **index** defined on the Table, it will be mapped to the secondary index's `sortKey` |

Expand Down
13 changes: 13 additions & 0 deletions src/__tests__/entity-creation.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ describe('Entity creation', () => {
expect(TestEntity.defaults).toHaveProperty('_ct')
expect(TestEntity.defaults).toHaveProperty('_md')
expect(TestEntity._etAlias).toBe('entity')
expect(TestEntity.typeHidden).toBe(false)
})

it('creates basic entity w/o timestamps', () => {
Expand Down Expand Up @@ -73,6 +74,18 @@ describe('Entity creation', () => {
expect(TestEntity.defaults).toHaveProperty('modifiedAt')
})

it('creates basic entity with typeHidden set to true', () => {
let TestEntity = new Entity({
name: 'TestEntity',
typeHidden: true,
attributes: {
pk: { partitionKey: true },
}
} as const)

expect(TestEntity.typeHidden).toBe(true)
})

it('creates basic entity w/ required fields', () => {
let TestEntity = new Entity({
name: 'TestEntity',
Expand Down
65 changes: 65 additions & 0 deletions src/__tests__/entity.parse.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,27 @@ const TestTable = new Table({
DocumentClient
})

const TestEntityHiddenType = new Entity({
name: 'TestEntityHiddenType',
attributes: {
pk: { type: 'string', partitionKey: true },
sk: { type: 'string', sortKey: true },
},
typeHidden: true,
table: TestTable
})

const TestEntityHiddenTypeWithAlias = new Entity({
name: 'TestEntityHiddenType',
attributes: {
pk: { type: 'string', partitionKey: true },
sk: { type: 'string', sortKey: true },
},
typeHidden: true,
typeAlias: 'TestEntityHiddenTypeAlias',
table: TestTable
})

describe('parse', () => {
it('parses single item', () => {
let item = TestEntity.parse({
Expand Down Expand Up @@ -45,6 +66,32 @@ describe('parse', () => {
})
})

it('parses single item and hide the entity type', () => {
let item = TestEntityHiddenType.parse({
pk: 'test@test.com',
sk: 'email',
_et: 'TestEntity',
})

expect(item).toEqual({
pk: 'test@test.com',
sk: 'email'
})
})

it('parses single item with alias and hide the entity type', () => {
let item = TestEntityHiddenTypeWithAlias.parse({
pk: 'test@test.com',
sk: 'email',
_et: 'TestEntity',
})

expect(item).toEqual({
pk: 'test@test.com',
sk: 'email'
})
})

it('parses multiple items', () => {
let items = TestEntity.parse([
{ pk: 'test@test.com', sk: 'email', test_string: 'test' },
Expand Down Expand Up @@ -84,6 +131,24 @@ describe('parse', () => {
])
})

it('parses multiple items and hide the entity type', () => {
let items = TestEntityHiddenType.parse([
{ pk: 'test@test.com', sk: 'email', _et: 'TestEntity' },
{ pk: 'test2@test.com', sk: 'email2', _et: 'TestEntity' }
])

expect(items).toEqual([
{
pk: 'test@test.com',
sk: 'email'
},
{
pk: 'test2@test.com',
sk: 'email2'
}
])
})

it('parses composite field', () => {
let item = SimpleEntity.parse({
pk: 'test@test.com',
Expand Down
2 changes: 2 additions & 0 deletions src/__tests__/parseEntity.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const entity = {
modified: '_modified',
modifiedAlias: 'modifiedAlias',
typeAlias: 'typeAlias',
typeHidden: true,
attributes: {
pk: { partitionKey: true },
sk: { sortKey: true },
Expand All @@ -33,6 +34,7 @@ describe('parseEntity', () => {
expect(ent.autoExecute).toBe(true)
expect(ent.autoParse).toBe(true)
expect(ent._etAlias).toBe('typeAlias')
expect(ent.typeHidden).toBe(true)
})

const nonObjectValues = [null, 'string', true, 1, [], () => {}]
Expand Down
12 changes: 10 additions & 2 deletions src/classes/Entity/Entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ class Entity<
CreatedAlias extends string = string extends Name ? string : 'created',
ModifiedAlias extends string = string extends Name ? string : 'modified',
TypeAlias extends string = string extends Name ? string : 'entity',
TypeHidden extends boolean = string extends Name ? boolean : false,
ReadonlyAttributeDefinitions extends Readonly<AttributeDefinitions> = Readonly<AttributeDefinitions>,
WritableAttributeDefinitions extends AttributeDefinitions = Writable<ReadonlyAttributeDefinitions>,
Attributes extends ParsedAttributes = string extends Name
Expand All @@ -74,7 +75,8 @@ class Entity<
Timestamps,
CreatedAlias,
ModifiedAlias,
TypeAlias
TypeAlias,
TypeHidden
>,
ParsedAttributes<keyof EntityItemOverlay>
>,
Expand Down Expand Up @@ -117,6 +119,7 @@ class Entity<
public createdAlias: CreatedAlias
public modifiedAlias: ModifiedAlias
public typeAlias: TypeAlias
public typeHidden: TypeHidden

// Declare constructor (entity config)
constructor(
Expand All @@ -129,6 +132,7 @@ class Entity<
CreatedAlias,
ModifiedAlias,
TypeAlias,
TypeHidden,
ReadonlyAttributeDefinitions
>
) {
Expand All @@ -142,13 +146,15 @@ class Entity<
timestamps = true,
createdAlias = 'created',
modifiedAlias = 'modified',
typeAlias = 'entity'
typeAlias = 'entity',
typeHidden = false
} = entity
this.attributes = attributes
this.timestamps = timestamps as Timestamps
this.createdAlias = createdAlias as CreatedAlias
this.modifiedAlias = modifiedAlias as ModifiedAlias
this.typeAlias = typeAlias as TypeAlias
this.typeHidden = typeHidden as TypeHidden
// Parse the entity and merge into this
Object.assign(this, parseEntity(entity))
} // end construcor
Expand All @@ -172,6 +178,7 @@ class Entity<
if (table.Table.entityField) {
this.schema.attributes[table.Table.entityField] = {
type: 'string',
hidden: this.typeHidden,
alias: this._etAlias,
default: this.name
}
Expand Down Expand Up @@ -400,6 +407,7 @@ class Entity<
CreatedAlias,
ModifiedAlias,
TypeAlias,
TypeHidden,
ReadonlyAttributeDefinitions,
WritableAttributeDefinitions,
Attributes,
Expand Down
9 changes: 7 additions & 2 deletions src/classes/Entity/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export interface EntityConstructor<
CreatedAlias extends string = 'created',
ModifiedAlias extends string = 'modified',
TypeAlias extends string = 'entity',
TypeHidden extends boolean = false,
ReadonlyAttributeDefinitions extends Readonly<AttributeDefinitions> = Readonly<AttributeDefinitions>
> {
table?: EntityTable
Expand All @@ -24,6 +25,7 @@ export interface EntityConstructor<
createdAlias?: CreatedAlias
modifiedAlias?: ModifiedAlias
typeAlias?: TypeAlias
typeHidden?: TypeHidden
attributes: ReadonlyAttributeDefinitions
autoExecute?: AutoExecute
autoParse?: AutoParse
Expand Down Expand Up @@ -146,6 +148,7 @@ export type ParseAttributes<
CreatedAlias extends string,
ModifiedAlias extends string,
TypeAlias extends string,
TypeHidden extends boolean,
Aliases extends string =
| (Timestamps extends true ? CreatedAlias | ModifiedAlias : never)
| TypeAlias,
Expand Down Expand Up @@ -532,7 +535,8 @@ export type InferEntityItem<
E['timestamps'],
E['createdAlias'],
E['modifiedAlias'],
E['typeAlias']
E['typeAlias'],
E['typeHidden']
>,
Item = InferItem<WritableAttributeDefinitions, Attributes>
> = Pick<Item, Extract<Attributes['shown'], keyof Item>>
Expand All @@ -552,7 +556,8 @@ export type ExtractAttributes<E extends Entity> =
E['timestamps'],
E['createdAlias'],
E['modifiedAlias'],
E['typeAlias']
E['typeAlias'],
E['typeHidden']
>

export type GetOptions<
Expand Down
10 changes: 9 additions & 1 deletion src/lib/parseEntity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,16 @@ export type ParsedEntity<
Name extends string = string,
AutoExecute extends boolean = boolean,
AutoParse extends boolean = boolean,
TypeAlias extends string = string
TypeAlias extends string = string,
TypeHidden extends boolean = boolean,
> = {
name: Name;
schema: {
keys: any;
attributes: Record<string, PureAttributeDefinition>;
};
_etAlias: TypeAlias;
typeHidden: TypeHidden;
autoParse: AutoParse | undefined;
autoExecute: AutoExecute | undefined;
linked: Linked;
Expand All @@ -63,6 +65,7 @@ export function parseEntity<
CreatedAlias extends string,
ModifiedAlias extends string,
TypeAlias extends string,
TypeHidden extends boolean,
ReadonlyAttributeDefinitions extends Readonly<AttributeDefinitions> = Readonly<AttributeDefinitions>
>(
entity: EntityConstructor<
Expand All @@ -74,6 +77,7 @@ export function parseEntity<
CreatedAlias,
ModifiedAlias,
TypeAlias,
TypeHidden,
ReadonlyAttributeDefinitions
>
): ParsedEntity<EntityTable, Name, AutoExecute, AutoParse, TypeAlias> {
Expand All @@ -85,6 +89,7 @@ export function parseEntity<
modified,
modifiedAlias,
typeAlias,
typeHidden,
attributes,
autoExecute,
autoParse,
Expand Down Expand Up @@ -132,6 +137,8 @@ export function parseEntity<
? typeAlias.trim()
: 'entity') as TypeAlias

typeHidden = (typeof typeHidden === 'boolean' ? typeHidden : false) as TypeHidden

// Sanity check the attributes
attributes =
attributes?.constructor === Object
Expand Down Expand Up @@ -172,6 +179,7 @@ export function parseEntity<
linked: track.linked,
autoExecute,
autoParse,
typeHidden,
_etAlias: typeAlias
},
table ? { table } : {}
Expand Down

0 comments on commit 6857873

Please sign in to comment.