Skip to content

Commit

Permalink
feat: Add transaction support. Passes transaction used by Models into…
Browse files Browse the repository at this point in the history
… the plugin.
  • Loading branch information
jarrodconnolly committed Aug 27, 2020
1 parent 50a6953 commit a1b726a
Show file tree
Hide file tree
Showing 3 changed files with 210 additions and 113 deletions.
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ SequelizeSlugify.slugifyModel(User, {
overwrite: false,
column: 'slug',
incrementalSeparator: '-',
passTransaction: true
});
```
Available Options
Expand All @@ -48,8 +49,8 @@ Available Options
- `slugOptions` - (Optional)(Default `{lower: true}`) Pass additional options for slug generation as defined by [`sluglife`](https://github.com/jarrodconnolly/sluglife#options).
- `overwrite` - (Optional)(Default `true`) Change the slug if the source fields change once the slug has already been built.
- `column` - (Optional)(Default `slug`) Specify which column the slug is to be stored into in the model.
- `incrementalSeparator` - (Default `-`) Specify the separator between the slug, and the duplicate count.

- `incrementalSeparator` - (Optional)(Default `-`) Specify the separator between the slug, and the duplicate count.
- `passTransaction` - (Optional)(Default `true`) Whether to pass an outer transaction, if one exists, to the plugin.
## Usage Examples

### Basic Usage
Expand Down Expand Up @@ -156,3 +157,12 @@ export default (sequelize, DataTypes) => {
return User;
};
```
## Transactions

A transaction wrapping operations on the Model will be passed by default to the internals of this plugin.
This behaviour can be modified using the `passTransaction` option described above.

Internally this plugin only calls a `findOne` operation, passing the transaction to this may help in specific Isolation scenarios depending on your underlying database.

Fields modified when creating/updating the slug will be rolled back even if we do not pass the transaction due to the nature of how hooks operate.

29 changes: 17 additions & 12 deletions lib/sequelize-slugify.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@ class SequelizeSlugify {
const DEFAULT_OPTIONS = {
overwrite: true,
column: 'slug',
incrementalSeparator: '-'
incrementalSeparator: '-',
passTransaction: true
};

const slugifyOptions = {...DEFAULT_OPTIONS, ...options};

const handleSlugify = async function (instance) {
const handleSlugify = async function (instance, sequelizeOptions) {

// check if any of the fields used to build the slug have changed
const changed = slugifyOptions.source.some(function (slugSourceField) {
Expand Down Expand Up @@ -50,24 +51,27 @@ class SequelizeSlugify {
return instance;
}

// obtain transaction if enabled
const transaction = (slugifyOptions.passTransaction === true) ? sequelizeOptions.transaction : null;

// determine if the slug is unique
let isUnique = await isUniqueSlug(slugValue);
let isUnique = await isUniqueSlug(slugValue, transaction);
if (isUnique) {
instance[slugColumn] = slugValue;
return instance;
}

// add on suffixes from the provided source suffixes
slugValue = await addSourceSuffix(instance, slugValue, slugifyOptions.suffixSource);
slugValue = await addSourceSuffix(instance, slugValue, slugifyOptions.suffixSource, transaction);

// determine if the slug is unique
isUnique = await isUniqueSlug(slugValue);
isUnique = await isUniqueSlug(slugValue, transaction);
if (isUnique) {
instance[slugColumn] = slugValue;
return instance;
}

slugValue = await addNumericSuffix(slugValue);
slugValue = await addNumericSuffix(slugValue, transaction);

instance[slugColumn] = slugValue;
return instance;
Expand All @@ -87,15 +91,16 @@ class SequelizeSlugify {
return slug(slugParts.join(' '), options);
};

const isUniqueSlug = async function (slug) {
const isUniqueSlug = async function (slug, transaction) {
const result = await model.findOne({
where: {[slugColumn]: slug }
where: {[slugColumn]: slug },
transaction: transaction
});
return result === null;
};

// optionally adding suffix's from other field values
const addSourceSuffix = async function (instance, slugValue, suffixFields) {
const addSourceSuffix = async function (instance, slugValue, suffixFields, transaction) {
// return current slug if no suffix provided
if (!suffixFields || !Array.isArray(suffixFields)) {
return slugValue;
Expand All @@ -110,21 +115,21 @@ class SequelizeSlugify {
suffixedSlug += `${slugifyOptions.incrementalSeparator}${newSlugSection}`;
}
// if the slug is now unique we can stop
const foundUnique = await isUniqueSlug(suffixedSlug);
const foundUnique = await isUniqueSlug(suffixedSlug, transaction);
if(foundUnique) {
break;
}
}
return suffixedSlug;
};

const addNumericSuffix = async function (slugValue) {
const addNumericSuffix = async function (slugValue, transaction) {
let count = 1;
let foundUnique = false;
let suffixedSlug = '';
while(!foundUnique) {
suffixedSlug = `${slugValue}${slugifyOptions.incrementalSeparator}${count}`;
foundUnique = await isUniqueSlug(suffixedSlug);
foundUnique = await isUniqueSlug(suffixedSlug, transaction);
count++;
}
return suffixedSlug;
Expand Down
Loading

0 comments on commit a1b726a

Please sign in to comment.