Skip to content

Commit

Permalink
Merge branch 'master' into documentRetrieverFix
Browse files Browse the repository at this point in the history
# Conflicts:
#	lib/utils/dynamoose/index.js
  • Loading branch information
fishcharlie committed Apr 24, 2020
2 parents a72cf9c + a286d1c commit 4336a25
Show file tree
Hide file tree
Showing 8 changed files with 386 additions and 93 deletions.
1 change: 1 addition & 0 deletions BREAKING_CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- `$DELETE` has been renamed to `$REMOVE`
- `$REMOVE` (previously `$DELETE`) now maps to the correct underlying DynamoDB method instead of the previous behavior of mapping to `$REMOVE`
- `$PUT` has been replaced with `$SET`
- Model `update` setting now includes more update actions. To use the v1 update behavior change the value of `update` setting to be `["ttl", "indexes"]`.
- `dynamoose.setDefaults` has been renamed to `dynamoose.model.defaults.set`
- `dynamoose.local` has been renamed to `dynamoose.aws.ddb.local`
- `dynamoose.setDDB` has been renamed to `dynamoose.aws.ddb.set`
Expand Down
6 changes: 3 additions & 3 deletions docs/docs/api/Model.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,16 @@ The `config` parameter is an object used to customize settings for the model.
|------|-------------|------|---------|
| create | If Dynamoose should attempt to create the table on DynamoDB. This function will run a `describeTable` call first to ensure the table doesn't already exist. For production environments we recommend setting this value to `false`. | Boolean | true |
| throughput | An object with settings for what the throughput for the table should be on creation, or a number which will use the same throughput for both read and write. If this is set to `ON_DEMAND` the table will use the `PAY_PER_REQUEST` billing mode. If the table is not created by Dynamoose, this object has no effect. | Object \| Number \| String | |
| throughput.read | What the read throughput should be set to. Only valid if `throughput` is an object. | Number | 5 |
| throughput.write | What the write throughput should be set to. Only valid if `throughput` is an object. | Number | 5 |
| throughput.read | What the read throughput should be set to. Only valid if `throughput` is an object. | Number | 1 |
| throughput.write | What the write throughput should be set to. Only valid if `throughput` is an object. | Number | 1 |
| prefix | A string that should be prepended to every model name. | String | "" |
| suffix | A string that should be appended to every model name. | String | "" |
| waitForActive | Settings for how DynamoDB should handle waiting for the table to be active before enabling actions to be run on the table. This property can also be set to `false` to easily disable the behavior of waiting for the table to be active. For production environments we recommend setting this value to `false`. | Object | |
| waitForActive.enabled | If Dynamoose should wait for the table to be active before running actions on it. | Boolean | true |
| waitForActive.check | Settings for how Dynamoose should check if the table is active | Object | |
| waitForActive.check.timeout | How many milliseconds before Dynamoose should timeout and stop checking if the table is active. | Number | 180000 |
| waitForActive.check.frequency | How many milliseconds Dynamoose should delay between checks to see if the table is active. If this number is set to 0 it will use `setImmediate()` to run the check again. | Number | 1000 |
| update | If Dynamoose should update the capacity of the existing table to match the model throughput. | Boolean | false |
| update | If Dynamoose should update the capacity of the existing table to match the model throughput. If this is a boolean of `true` all update actions will be run. If this is an array of strings, only the actions in the array will be run. The array can include the following items to update, `ttl`, `indexes`, `throughput`. | Boolean \| [String] | false |
| expires | The setting to describe the time to live for documents created. If you pass in a number it will be used for the `expires.ttl` setting, with default values for everything else. If this is `null`, no time to live will be active on the model. | Number \| Object | null |
| expires.ttl | The default amount of time the document should stay alive from creation time in milliseconds. | Number | null |
| expires.attribute | The attribute name for where the document time to live attribute. | String | `ttl` |
Expand Down
6 changes: 3 additions & 3 deletions lib/Model/defaults.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
const original = {
"create": true,
"throughput": {
"read": 5,
"write": 5
"read": 1,
"write": 1
},
"prefix": "",
"suffix": "",
Expand All @@ -29,4 +29,4 @@ module.exports = {
"get": () => custom
},
original
};
};
52 changes: 39 additions & 13 deletions lib/Model/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class Model {

// Setup flow
this.ready = false; // Represents if model is ready to be used for actions such as "get", "put", etc. This property being true does not guarantee anything on the DynamoDB server. It only guarantees that Dynamoose has finished the initalization steps required to allow the model to function as expected on the client side.
this.alreadyCreated = false; // Represents if the table in DynamoDB was created prior to initalization. This will only be updated if `create` is true.
this.pendingTasks = []; // Represents an array of promise resolver functions to be called when Model.ready gets set to true (at the end of the setup flow)
this.latestTableDetails = null; // Stores the latest result from `describeTable` for the given table
this.pendingTaskPromise = () => { // Returns a promise that will be resolved after the Model is ready. This is used in all Model operations (Model.get, Document.save) to `await` at the beginning before running the AWS SDK method to ensure the Model is setup before running actions on it.
Expand All @@ -60,11 +61,11 @@ class Model {
setupFlow.push(() => waitForActive(this));
}
// Update Time To Live
if ((this.options.create || this.options.update) && options.expires) {
if ((this.options.create || (Array.isArray(this.options.update) ? this.options.update.includes("ttl") : this.options.update)) && options.expires) {
setupFlow.push(() => updateTimeToLive(this));
}
// Update
if (this.options.update) {
if (this.options.update && !this.alreadyCreated) {
setupFlow.push(() => updateTable(this));
}

Expand Down Expand Up @@ -134,6 +135,7 @@ class Model {
// Utility functions
async function createTable(model) {
if ((((await getTableDetails(model, {"allowError": true})) || {}).Table || {}).TableStatus === "ACTIVE") {
model.alreadyCreated = true;
return () => Promise.resolve.bind(Promise)();
}

Expand Down Expand Up @@ -223,18 +225,42 @@ async function getTableDetails(model, settings = {}) {
return model.latestTableDetails;
}
async function updateTable(model) {
const currentThroughput = (await getTableDetails(model)).Table;
const expectedThroughput = utils.dynamoose.get_provisioned_throughput(model.options);
if ((expectedThroughput.BillingMode === currentThroughput.BillingMode && expectedThroughput.BillingMode) || ((currentThroughput.ProvisionedThroughput || {}).ReadCapacityUnits === (expectedThroughput.ProvisionedThroughput || {}).ReadCapacityUnits && currentThroughput.ProvisionedThroughput.WriteCapacityUnits === expectedThroughput.ProvisionedThroughput.WriteCapacityUnits)) {
// if ((expectedThroughput.BillingMode === currentThroughput.BillingModeSummary.BillingMode && expectedThroughput.BillingMode) || ((currentThroughput.ProvisionedThroughput || {}).ReadCapacityUnits === (expectedThroughput.ProvisionedThroughput || {}).ReadCapacityUnits && currentThroughput.ProvisionedThroughput.WriteCapacityUnits === expectedThroughput.ProvisionedThroughput.WriteCapacityUnits)) {
return () => Promise.resolve.bind(Promise)();
const updateAll = typeof model.options.update === "boolean" && model.options.update;
// Throughput
if (updateAll || model.options.update.includes("throughput")) {
const currentThroughput = (await getTableDetails(model)).Table;
const expectedThroughput = utils.dynamoose.get_provisioned_throughput(model.options);
const isThroughputUpToDate = (expectedThroughput.BillingMode === currentThroughput.BillingMode && expectedThroughput.BillingMode) || ((currentThroughput.ProvisionedThroughput || {}).ReadCapacityUnits === (expectedThroughput.ProvisionedThroughput || {}).ReadCapacityUnits && currentThroughput.ProvisionedThroughput.WriteCapacityUnits === expectedThroughput.ProvisionedThroughput.WriteCapacityUnits);

if (!isThroughputUpToDate) {
const params = {
"TableName": model.name,
...expectedThroughput
};
await ddb("updateTable", params);
await waitForActive(model);
}
}
// Indexes
if (updateAll || model.options.update.includes("indexes")) {
const tableDetails = await getTableDetails(model);
const existingIndexes = tableDetails.GlobalSecondaryIndexes;
const updateIndexes = await utils.dynamoose.index_changes(model, existingIndexes);
await updateIndexes.reduce(async (existingFlow, index) => {
await existingFlow;
const params = {
"TableName": model.name
};
if (index.type === "add") {
params.AttributeDefinitions = (await model.schema.getCreateTableAttributeParams(model)).AttributeDefinitions;
params.GlobalSecondaryIndexUpdates = [{"Create": index.spec}];
} else {
params.GlobalSecondaryIndexUpdates = [{"Delete": {"IndexName": index.name}}];
}
await ddb("updateTable", params);
await waitForActive(model);
}, Promise.resolve());
}

const object = {
"TableName": model.name,
...expectedThroughput
};
return ddb("updateTable", object);
}

function convertObjectToKey(key) {
Expand Down
1 change: 1 addition & 0 deletions lib/utils/dynamoose/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ module.exports = {
"wildcard_allowed_check": require("./wildcard_allowed_check"),
"get_provisioned_throughput": require("./get_provisioned_throughput"),
"convertConditionArrayRequestObjectToString": require("./convertConditionArrayRequestObjectToString"),
"index_changes": require("./index_changes")
};
17 changes: 17 additions & 0 deletions lib/utils/dynamoose/index_changes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const obj = require("../object");

module.exports = async (model, existingIndexes = []) => {
const output = [];
const expectedIndexes = await model.schema.getIndexes(model);

// Indexes to delete
output.push(...existingIndexes.filter((index) => !(expectedIndexes.GlobalSecondaryIndexes || []).find((searchIndex) => obj.equals(index, searchIndex))).map((index) => ({"name": index.IndexName, "type": "delete"})));

// Indexes to create
output.push(...(expectedIndexes.GlobalSecondaryIndexes || []).filter((index) => ![...output.map((i) => i.name), ...existingIndexes.map((i) => i.IndexName)].includes(index.IndexName)).map((index) => ({
"type": "add",
"spec": index
})));

return output;
};
Loading

0 comments on commit 4336a25

Please sign in to comment.