@ginger.io/beyonce
Advanced tools
Comparing version 0.0.50 to 0.0.51
@@ -21,13 +21,2 @@ "use strict"; | ||
}; | ||
var __rest = (this && this.__rest) || function (s, e) { | ||
var t = {}; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) | ||
t[p] = s[p]; | ||
if (s != null && typeof Object.getOwnPropertySymbols === "function") | ||
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { | ||
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) | ||
t[p[i]] = s[p[i]]; | ||
} | ||
return t; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
@@ -89,3 +78,3 @@ return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
parser: "typescript", | ||
semi: false, | ||
semi: false | ||
}); | ||
@@ -96,3 +85,3 @@ } | ||
const tables = []; | ||
Object.entries(config.Tables).forEach(([name, { Partitions, GSIs }]) => { | ||
Object.entries(config.tables).forEach(([name, { models, partitions, gsis }]) => { | ||
const table = { | ||
@@ -102,7 +91,7 @@ name, | ||
sortKeyName: "sk", | ||
partitions: toPartitions(name, Partitions), | ||
gsis: [], | ||
partitions: buildPartitions(name, models, partitions), | ||
gsis: [] | ||
}; | ||
if (GSIs !== undefined) { | ||
Object.entries(GSIs).forEach(([name, { partitionKey, sortKey }]) => { | ||
if (gsis !== undefined) { | ||
Object.entries(gsis).forEach(([name, { partitionKey, sortKey }]) => { | ||
table.gsis.push({ name, partitionKey, sortKey }); | ||
@@ -115,21 +104,27 @@ }); | ||
} | ||
function toPartitions(tableName, partitionDefs) { | ||
const partitions = Object.entries(partitionDefs).map(([partitionName, partition]) => { | ||
const models = Object.entries(partition).map(([modelName, model]) => { | ||
const { partitionKey, sortKey } = model, fields = __rest(model, ["partitionKey", "sortKey"]); | ||
return { | ||
tableName, | ||
partitionName, | ||
name: modelName, | ||
keys: { partitionKey, sortKey }, | ||
fields, | ||
}; | ||
}); | ||
function buildPartitions(tableName, modelDefinitions, partitionDefinitions) { | ||
return Object.entries(partitionDefinitions).map(([partitionName, partition]) => ({ | ||
tableName, | ||
name: partitionName, | ||
models: buildModels(tableName, partitionName, partition, modelDefinitions) | ||
})); | ||
} | ||
function buildModels(tableName, partitionName, partition, modelDefinitions) { | ||
return Object.entries(partition.models).map(([modelName, { partitionKey, sortKey }]) => { | ||
const partitionKeyWithPrefix = [ | ||
partition.partitionKeyPrefix, | ||
...partitionKey | ||
]; | ||
const fields = modelDefinitions[modelName]; | ||
if (!fields) { | ||
throw new Error(`${modelName} is defined in a YAML partition, but does not appear in your models list`); | ||
} | ||
return { | ||
tableName, | ||
name: partitionName, | ||
models, | ||
partitionName, | ||
name: modelName, | ||
keys: { partitionKey: partitionKeyWithPrefix, sortKey }, | ||
fields | ||
}; | ||
}); | ||
return partitions; | ||
} | ||
@@ -136,0 +131,0 @@ function parseYaml(yamlData) { |
@@ -10,3 +10,3 @@ "use strict"; | ||
const fieldToModels = { | ||
model: [], | ||
model: [] | ||
}; | ||
@@ -16,3 +16,3 @@ const fieldsAllModelsHave = { | ||
[table.sortKeyName]: "string", | ||
model: "string", | ||
model: "string" | ||
}; | ||
@@ -19,0 +19,0 @@ table.partitions.forEach((partition) => { |
export declare type YAMLFile = { | ||
Tables: { | ||
tables: { | ||
[tableName: string]: TableDefinition; | ||
@@ -7,6 +7,9 @@ }; | ||
export declare type TableDefinition = { | ||
Partitions: { | ||
models: { | ||
[modelName: string]: ModelDefinition; | ||
}; | ||
partitions: { | ||
[partitionName: string]: PartitionDefinition; | ||
}; | ||
GSIs?: { | ||
gsis?: { | ||
[indexName: string]: GSIDefinition; | ||
@@ -16,10 +19,12 @@ }; | ||
export declare type PartitionDefinition = { | ||
[modelName: string]: ModelDefinition; | ||
partitionKeyPrefix: string; | ||
models: { | ||
[modelName: string]: ModelKeys; | ||
}; | ||
}; | ||
export declare type ModelDefinition = Keys & Fields; | ||
export declare type Keys = { | ||
export declare type ModelKeys = { | ||
partitionKey: string[]; | ||
sortKey: string[]; | ||
}; | ||
export declare type Fields = { | ||
export declare type ModelDefinition = { | ||
[fieldName: string]: string; | ||
@@ -52,5 +57,5 @@ }; | ||
name: string; | ||
keys: Keys; | ||
fields: Fields; | ||
keys: ModelKeys; | ||
fields: ModelDefinition; | ||
}; | ||
//# sourceMappingURL=types.d.ts.map |
{ | ||
"name": "@ginger.io/beyonce", | ||
"version": "0.0.50", | ||
"version": "0.0.51", | ||
"description": "Type-safe DynamoDB query builder for TypeScript. Designed with single-table architecture in mind.", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
@@ -26,22 +26,45 @@ # Beyonce | ||
Define your `tables`, `partitions` and `models` in YAML: | ||
Define your `tables`, `models` and `partitions` in YAML: | ||
```YAML | ||
Tables: | ||
tables: | ||
# We have a single DynamoDB Table named "Library". | ||
Library: | ||
Partitions: # A "partition" is a set of models with the same partition key | ||
Authors: # e.g. this one contains Authors + Books | ||
Author: | ||
partitionKey: [Author, $id] | ||
sortKey: [Author, $id] | ||
id: string | ||
name: string | ||
# Let's add two models to our Library table: Author and Book. | ||
models: | ||
Author: | ||
id: string | ||
name: string | ||
Book: | ||
partitionKey: [Author, $authorId] | ||
sortKey: [Book, $id] | ||
id: string | ||
authorId: string | ||
name: string | ||
Book: | ||
id: string | ||
authorId: string | ||
name: string | ||
# Now, imagine we want to be able to retrieve an Author + all their Books | ||
# in a single DynamoDB Query operation. | ||
# To do that, we need a specific Author and all their Books to live under the same partition key. | ||
# How about we use "Author-$id" as the partition key? Great, let's go with that. | ||
# Beyonce calls a group of models that share the same partition key a "patition". | ||
# Let's define one now, and name it "Authors" | ||
partitions: | ||
Authors: | ||
# All Beyonce partition keys are prefixed (to help you avoid collisions) | ||
# We said above we want our final partition key to be "Author-$id", | ||
# so we set: "Author" as our prefix here | ||
partitionKeyPrefix: Author | ||
# And, now we can put a given Author and all their Books into the same partition | ||
models: | ||
Author: | ||
partitionKey: [$id] # "Author-$id" | ||
sortKey: [Author, $id] | ||
Book: | ||
partitionKey: [$authorId] # "Author-$authorId" | ||
sortKey: [Book, $id] | ||
``` | ||
@@ -51,6 +74,8 @@ | ||
Beyonce expects you to specify your partition and sort keys as arrays, e.g. `[Author, $id]`. The first element is a "key prefix" and all subsequent items must be field names on your model. For example we set the primary key of the `Author` model above to: `[Author, $id]`, would result in the key: `Author-$id`, where `$id` is the value of a specific Author's id. And if we wanted a compound key we could do `[Author, $id, $name]`. You can specify compound keys for both partition and sort keys. | ||
Beyonce expects you to specify your partition and sort keys using arrays, e.g. `[Author, $id]`. The first element in this example is interpreted as a string literal, while the second substitutes the value of a specific model instance's `id` field. In addition, Beyonce prefixes partition keys with the `partitionKeyPrefix` set on the Beyonce "partition" configured your the YAML file. | ||
Using the example above, if we wanted to place `Books` under the same partition key, then we'd need to set the `Book` model's `partitionKey` to `[Author, $authorId]`. | ||
In our example above, we set the `Author` partiion's `partitionKeyPrefix` to `"Author"` and the `Author` model's `partitionKey` field to `[$id]`. Thus the full partition key at runtime is `Author-$id` (Beyonce uses `-` as a delimiter). | ||
If you'd like to form a composite partition or sort key using multiple model fields, that is supported as well, e.g. `[$id, $name]`. | ||
#### Global secondary indexes | ||
@@ -61,8 +86,10 @@ | ||
```YAML | ||
Tables: | ||
tables: | ||
Library: | ||
Partitions: | ||
models: | ||
... | ||
partitions: | ||
... | ||
GSIs: | ||
gsis: | ||
byName: # must match your GSI's name | ||
@@ -69,0 +96,0 @@ partitionKey: $name # name field must exist on at least one model |
@@ -11,3 +11,10 @@ import yaml from "js-yaml" | ||
import { generateTaggedUnion } from "./generateTaggedUnion" | ||
import { Model, Partition, Table, TableDefinition, YAMLFile } from "./types" | ||
import { | ||
Model, | ||
Partition, | ||
PartitionDefinition, | ||
Table, | ||
TableDefinition, | ||
YAMLFile | ||
} from "./types" | ||
@@ -61,3 +68,3 @@ export function generateCode(yamlData: string): string { | ||
parser: "typescript", | ||
semi: false, | ||
semi: false | ||
}) | ||
@@ -69,49 +76,68 @@ } | ||
Object.entries(config.Tables).forEach(([name, { Partitions, GSIs }]) => { | ||
const table: Table = { | ||
name, | ||
partitionKeyName: "pk", | ||
sortKeyName: "sk", | ||
partitions: toPartitions(name, Partitions), | ||
gsis: [], | ||
} | ||
Object.entries(config.tables).forEach( | ||
([name, { models, partitions, gsis }]) => { | ||
const table: Table = { | ||
name, | ||
partitionKeyName: "pk", | ||
sortKeyName: "sk", | ||
partitions: buildPartitions(name, models, partitions), | ||
gsis: [] | ||
} | ||
if (GSIs !== undefined) { | ||
Object.entries(GSIs).forEach(([name, { partitionKey, sortKey }]) => { | ||
table.gsis.push({ name, partitionKey, sortKey }) | ||
}) | ||
if (gsis !== undefined) { | ||
Object.entries(gsis).forEach(([name, { partitionKey, sortKey }]) => { | ||
table.gsis.push({ name, partitionKey, sortKey }) | ||
}) | ||
} | ||
tables.push(table) | ||
} | ||
) | ||
tables.push(table) | ||
}) | ||
return tables | ||
} | ||
function toPartitions( | ||
function buildPartitions( | ||
tableName: string, | ||
partitionDefs: TableDefinition["Partitions"] | ||
modelDefinitions: TableDefinition["models"], | ||
partitionDefinitions: TableDefinition["partitions"] | ||
): Partition[] { | ||
const partitions = Object.entries(partitionDefs).map( | ||
([partitionName, partition]) => { | ||
const models = Object.entries(partition).map(([modelName, model]) => { | ||
const { partitionKey, sortKey, ...fields } = model | ||
return { | ||
tableName, | ||
partitionName, | ||
name: modelName, | ||
keys: { partitionKey, sortKey }, | ||
fields, | ||
} | ||
}) | ||
return Object.entries(partitionDefinitions).map( | ||
([partitionName, partition]) => ({ | ||
tableName, | ||
name: partitionName, | ||
models: buildModels(tableName, partitionName, partition, modelDefinitions) | ||
}) | ||
) | ||
} | ||
function buildModels( | ||
tableName: string, | ||
partitionName: string, | ||
partition: PartitionDefinition, | ||
modelDefinitions: TableDefinition["models"] | ||
): Model[] { | ||
return Object.entries(partition.models).map( | ||
([modelName, { partitionKey, sortKey }]) => { | ||
const partitionKeyWithPrefix = [ | ||
partition.partitionKeyPrefix, | ||
...partitionKey | ||
] | ||
const fields = modelDefinitions[modelName] | ||
if (!fields) { | ||
throw new Error( | ||
`${modelName} is defined in a YAML partition, but does not appear in your models list` | ||
) | ||
} | ||
return { | ||
tableName, | ||
name: partitionName, | ||
models, | ||
partitionName, | ||
name: modelName, | ||
keys: { partitionKey: partitionKeyWithPrefix, sortKey }, | ||
fields | ||
} | ||
} | ||
) | ||
return partitions | ||
} | ||
@@ -118,0 +144,0 @@ |
@@ -1,2 +0,2 @@ | ||
import { Fields, Table } from "./types" | ||
import { ModelDefinition, Table } from "./types" | ||
@@ -9,9 +9,9 @@ export function generateGSIs(tables: Table[]): string { | ||
const fieldToModels: { [fieldName: string]: string[] } = { | ||
model: [], | ||
model: [] | ||
} | ||
const fieldsAllModelsHave: Fields = { | ||
const fieldsAllModelsHave: ModelDefinition = { | ||
[table.partitionKeyName]: "string", | ||
[table.sortKeyName]: "string", | ||
model: "string", | ||
model: "string" | ||
} | ||
@@ -18,0 +18,0 @@ |
export type YAMLFile = { | ||
Tables: { | ||
tables: { | ||
[tableName: string]: TableDefinition | ||
@@ -8,16 +8,22 @@ } | ||
export type TableDefinition = { | ||
Partitions: { | ||
models: { | ||
[modelName: string]: ModelDefinition | ||
} | ||
partitions: { | ||
[partitionName: string]: PartitionDefinition | ||
} | ||
GSIs?: { [indexName: string]: GSIDefinition } | ||
gsis?: { [indexName: string]: GSIDefinition } | ||
} | ||
export type PartitionDefinition = { | ||
[modelName: string]: ModelDefinition | ||
partitionKeyPrefix: string | ||
models: { | ||
[modelName: string]: ModelKeys | ||
} | ||
} | ||
export type ModelDefinition = Keys & Fields | ||
export type Keys = { partitionKey: string[]; sortKey: string[] } | ||
export type Fields = { [fieldName: string]: string } | ||
export type ModelKeys = { partitionKey: string[]; sortKey: string[] } | ||
export type ModelDefinition = { [fieldName: string]: string } | ||
@@ -53,4 +59,4 @@ export type GSIDefinition = { | ||
name: string | ||
keys: Keys | ||
fields: Fields | ||
keys: ModelKeys | ||
fields: ModelDefinition | ||
} |
@@ -5,11 +5,15 @@ import { generateCode } from "../../main/codegen/generateCode" | ||
const result = generateCode(` | ||
Tables: | ||
tables: | ||
Library: | ||
Partitions: | ||
models: | ||
Author: | ||
id: string | ||
name: string | ||
partitions: | ||
Authors: | ||
Author: | ||
partitionKey: [Author, $id] | ||
sortKey: [Author, $id] | ||
id: string | ||
name: string | ||
partitionKeyPrefix: Author | ||
models: | ||
Author: | ||
partitionKey: [$id] | ||
sortKey: [Author, $id] | ||
`) | ||
@@ -48,18 +52,25 @@ | ||
const result = generateCode(` | ||
Tables: | ||
tables: | ||
Library: | ||
Partitions: | ||
models: | ||
Author: | ||
id: string | ||
name: string | ||
Book: | ||
id: string | ||
authorId: string | ||
name: string | ||
partitions: | ||
Authors: | ||
Author: | ||
partitionKey: [Author, $id] | ||
sortKey: [Author, $id] | ||
id: string | ||
name: string | ||
partitionKeyPrefix: Author | ||
models: | ||
Author: | ||
partitionKey: [$id] | ||
sortKey: [Author, $id] | ||
Book: | ||
partitionKey: [Author, $authorId] | ||
sortKey: [Book, $id] | ||
id: string | ||
authorId: string | ||
name: string | ||
Book: | ||
partitionKey: [$authorId] | ||
sortKey: [Book, $id] | ||
`) | ||
@@ -110,21 +121,32 @@ | ||
const result = generateCode(` | ||
Tables: | ||
tables: | ||
Library: | ||
Partitions: | ||
models: | ||
Author: | ||
id: string | ||
name: string | ||
userId: string | ||
partitions: | ||
Authors: | ||
Author: | ||
partitionKey: [Author, $id] | ||
sortKey: [Author, $userId] | ||
id: string | ||
name: string | ||
userId: string | ||
partitionKeyPrefix: Author | ||
models: | ||
Author: | ||
partitionKey: [$id] | ||
sortKey: [Author, $userId] | ||
Music: | ||
Partitions: | ||
models: | ||
Musician: | ||
id: string | ||
name: string | ||
musicianId: string | ||
partitions: | ||
Musicians: | ||
Musician: | ||
partitionKey: [Musician, $id] | ||
sortKey: [Musician, $musicianId] | ||
id: string | ||
name: string | ||
musicianId: string | ||
partitionKeyPrefix: Musician | ||
models: | ||
Musician: | ||
partitionKey: [$id] | ||
sortKey: [Musician, $musicianId] | ||
`) | ||
@@ -184,19 +206,26 @@ | ||
const result = generateCode(` | ||
Tables: | ||
tables: | ||
Library: | ||
Partitions: | ||
models: | ||
Author: | ||
id: string | ||
name: string | ||
Book: | ||
id: string | ||
name: string | ||
partitions: | ||
Authors: | ||
Author: | ||
partitionKey: [Author, $id] | ||
sortKey: [Author, $id] | ||
id: string | ||
name: string | ||
partitionKeyPrefix: Author | ||
models: | ||
Author: | ||
partitionKey: [$id] | ||
sortKey: [Author, $id] | ||
Book: | ||
partitionKey: [Author, $id] | ||
sortKey: [Book, $id] | ||
id: string | ||
name: string | ||
Book: | ||
partitionKey: [$id] | ||
sortKey: [Book, $id] | ||
GSIs: | ||
gsis: | ||
modelById: | ||
@@ -218,19 +247,26 @@ partitionKey: $model | ||
const result = generateCode(` | ||
Tables: | ||
tables: | ||
Library: | ||
Partitions: | ||
models: | ||
Author: | ||
id: string | ||
name: string | ||
Book: | ||
id: string | ||
name: string | ||
partitions: | ||
Authors: | ||
Author: | ||
partitionKey: [Author, $id] | ||
sortKey: [Author, $id] | ||
id: string | ||
name: string | ||
partitionKeyPrefix: Author | ||
models: | ||
Author: | ||
partitionKey: [$id] | ||
sortKey: [Author, $id] | ||
Book: | ||
partitionKey: [Author, $id] | ||
sortKey: [Book, $id] | ||
id: string | ||
name: string | ||
Book: | ||
partitionKey: [$id] | ||
sortKey: [Book, $id] | ||
GSIs: | ||
gsis: | ||
modelById: | ||
@@ -251,19 +287,26 @@ partitionKey: $model | ||
const result = generateCode(` | ||
Tables: | ||
tables: | ||
Library: | ||
Partitions: | ||
models: | ||
Author: | ||
id: string | ||
name: string | ||
Book: | ||
id: string | ||
name: string | ||
partitions: | ||
Authors: | ||
Author: | ||
partitionKey: [Author, $id] | ||
sortKey: [Author, $id] | ||
id: string | ||
name: string | ||
partitionKeyPrefix: Author | ||
models: | ||
Author: | ||
partitionKey: [$id] | ||
sortKey: [Author, $id] | ||
Book: | ||
partitionKey: [Author, $id] | ||
sortKey: [Book, $id] | ||
id: string | ||
name: string | ||
Book: | ||
partitionKey: [$id] | ||
sortKey: [Book, $id] | ||
GSIs: | ||
gsis: | ||
byNameAndId: | ||
@@ -283,19 +326,25 @@ partitionKey: $name | ||
const result = generateCode(` | ||
Tables: | ||
tables: | ||
Library: | ||
Partitions: | ||
models: | ||
Author: | ||
id: string | ||
name: string | ||
Book: | ||
id: string | ||
name: string | ||
partitions: | ||
Authors: | ||
Author: | ||
partitionKey: [Author, $id] | ||
sortKey: [Author, $id] | ||
id: string | ||
name: string | ||
partitionKeyPrefix: Author | ||
models: | ||
Author: | ||
partitionKey: [$id] | ||
sortKey: [Author, $id] | ||
Book: | ||
partitionKey: [Author, $id] | ||
sortKey: [Book, $id] | ||
id: string | ||
name: string | ||
GSIs: | ||
Book: | ||
partitionKey: [$id] | ||
sortKey: [Book, $id] | ||
gsis: | ||
byNameAndId: | ||
@@ -315,11 +364,16 @@ partitionKey: $sk | ||
const result = generateCode(` | ||
Tables: | ||
tables: | ||
Library: | ||
Partitions: | ||
models: | ||
Author: | ||
id: string | ||
name: BestNameEvah from @cool.io/some/sweet/package | ||
partitions: | ||
Authors: | ||
Author: | ||
partitionKey: [Author, $id] | ||
sortKey: [Author, $id] | ||
id: string | ||
name: BestNameEvah from @cool.io/some/sweet/package | ||
partitionKeyPrefix: Author | ||
models: | ||
Author: | ||
partitionKey: [$id] | ||
sortKey: [Author, $id] | ||
`) | ||
@@ -337,11 +391,16 @@ | ||
const result = generateCode(` | ||
Tables: | ||
tables: | ||
ComplexLibrary: | ||
Partitions: | ||
models: | ||
ComplexAuthor: | ||
id: string | ||
name: string | ||
partitions: | ||
ComplexAuthors: | ||
ComplexAuthor: | ||
partitionKey: [Author, $id] | ||
sortKey: [Author, $id, $name] | ||
id: string | ||
name: string | ||
partitionKeyPrefix: Author | ||
models: | ||
ComplexAuthor: | ||
partitionKey: [$id] | ||
sortKey: [Author, $id, $name] | ||
`) | ||
@@ -348,0 +407,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
259139
4700
447