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

New: added migration scripts #81

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open

New: added migration scripts #81

wants to merge 4 commits into from

Conversation

danielghost
Copy link

Fixes #80.

New

  • Added migration scripts.

…d updated tasks accordingly. Fixed an issue with `ariaRegion` not always being updated to 6.1.3.
@danielghost
Copy link
Author

Copy link
Contributor

@joe-allen-89 joe-allen-89 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few changes from best practise from other repos and a couple of linting issues. @danielghost the majority of changes are the same across the other repos you've picked up, do you want me to leave the others until you've had chance to make the changes?

migrations/v2.js Outdated
Comment on lines 3 to 18
function hasKey(object, key) {
if (!object) return false;
return Object.hasOwn(object, key);
}

function setObjectPathValue(object, path, value, force = false) {
if (!object) return;
const paths = path.split('.');
const key = paths.pop();
const target = paths.reduce((o, p) => {
if (!hasKey(o, p)) o[p] = {};
return o?.[p];
}, object);
if (!force && hasKey(target, key)) return;
target[key] = value;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whilst these work perfectly, after discussing helper functions we've gone with using lodash as the standard with these functions already built in, we can therefore use _.has and _.set to save ourselves adding helper functions. I'll not raise this for each file.

Suggested change
function hasKey(object, key) {
if (!object) return false;
return Object.hasOwn(object, key);
}
function setObjectPathValue(object, path, value, force = false) {
if (!object) return;
const paths = path.split('.');
const key = paths.pop();
const target = paths.reduce((o, p) => {
if (!hasKey(o, p)) o[p] = {};
return o?.[p];
}, object);
if (!force && hasKey(target, key)) return;
target[key] = value;
}
import _ from 'lodash'

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #81 (comment) to confirm we prefer to go this route.

migrations/v2.js Outdated
if (!isValid) throw new Error('_globals ariaRegion not added');
return true;
});
updatePlugin('adapt-contrib-text - update to v2.0.1', {name: 'adapt-contrib-text', version: '2.0.1', framework: '>=2.0.0'})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
updatePlugin('adapt-contrib-text - update to v2.0.1', {name: 'adapt-contrib-text', version: '2.0.1', framework: '>=2.0.0'})
updatePlugin('adapt-contrib-text - update to v2.0.1', { name: 'adapt-contrib-text', version: '2.0.1', framework: '>=2.0.0' })

migrations/v2.js Outdated
let course;
whereContent('adapt-contrib-text - where missing _globals ariaRegion', async content => {
course = content.find(({ _type }) => _type === 'course');
return !hasKey(course?._globals?._components?._text, 'ariaRegion');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned we'll be changing to _.has (I'll not raise a comment for every instance),

Suggested change
return !hasKey(course?._globals?._components?._text, 'ariaRegion');
return !_.has(course, '_globals._components._text.ariaRegion');

migrations/v2.js Outdated
return !hasKey(course?._globals?._components?._text, 'ariaRegion');
});
mutateContent('adapt-contrib-text - add _globals ariaRegion', async () => {
setObjectPathValue(course, '_globals._components._text.ariaRegion', '');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again I'll not raise one for every instance but update setObjectPathValue to _.set

Suggested change
setObjectPathValue(course, '_globals._components._text.ariaRegion', '');
_.set(course, '_globals._components._text.ariaRegion', '');

Copy link
Author

@danielghost danielghost Feb 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If an author has changed the value from the default, we shouldn't apply the new default value. This would mean adding extra conditions before the set call, whereas setObjectPathValue takes care of this automatically:

if (!force && hasKey(target, key)) return;

Would we rather add these extra conditions in and use lodash instead? There could be many instances of needing this check throughout all the plugin migrations:

if (!_.has(course, '_globals._components._text.ariaRegion')) _.set(course, '_globals._components._text.ariaRegion', '');

Switching to use the lodash method works in the v2 migration as the whereContent checks to see if the attribute is present first and returns early if so, but the v6 migration doesn't have this because it potentially needs to do this additional step regardless of whether the new attribute is present:

delete course?._globals?._text;

Copy link
Member

@oliverfoster oliverfoster Feb 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Original discussion: adaptlearning/adapt-migrations#28 (comment)

The idea is to only add helper functions that have behaviour which is easily described by its name and which isn't already available as a known api.

function setObjectPathValue(object, path, value, force = false) {
if (!object) return;
const paths = path.split('.');
const key = paths.pop();
const target = paths.reduce((o, p) => {
if (!hasKey(o, p)) o[p] = {};
return o?.[p];
}, object);
if (!force && hasKey(target, key)) return;
target[key] = value;
}

setObjectPathValue uses value as a default value unless force=true. You would probably need to remove the force argument and have two functions, setObjectPathDefaultValue and setObjectPathValue in order to have self descriptive helper functions.

lodash is already installed and has got setObjectPathValue as _.set and hasKey as _.has, it just doesn't have setObjectPathDefaultValue.

lodash also has many more useful helper functions and documentation, making it much easier to reference and more useful and more widely beneficial for everyone to understand.

It is a little longer, but you can achieve a similar and simpler effect with lodash, without writing helpers:

const ariaRegionPath = '_globals._components._text.ariaRegion'
const ariaRegionDefault = ''
if (!_.has(course, ariaRegionPath)) {
  _.set(course, ariaRegionPath, ariaRegionDefault);
}

Or alternatively:

const ariaRegionPath = '_globals._components._text.ariaRegion'
const ariaRegionDefault = ''
_.set(course, ariaRegionPath, _.get(course, ariaRegionPath, ariaRegionDefault));

Or with lodash as a helper:

function setDefault(object, path, defaultValue) {
  if (_.has(object, path)) return
  _.set(object, path, defaultValue);
}

I know @joe-allen-89 has had discussions with others about the other question If an author has changed the value from the default, we shouldn't apply the new default value. I'm sure he has some references or advice in that regard.

Examples of simple helpers which don't exist externally, getConfig, getCourse, getComponents: https://github.com/adaptlearning/adapt-migrations/blob/a4b7824e0b4a9c588bb18bbd107ed79fa8a8f1d3/api/helpers.js#L7-L17

migrations/v2.js Outdated
@@ -0,0 +1,37 @@
import { describe , whereContent, whereFromPlugin, whereToPlugin, mutateContent, checkContent, updatePlugin } from 'adapt-migrations';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Picky I know but the same in both v2 and v6

Suggested change
import { describe , whereContent, whereFromPlugin, whereToPlugin, mutateContent, checkContent, updatePlugin } from 'adapt-migrations';
import { describe, whereContent, whereFromPlugin, mutateContent, checkContent, updatePlugin } from 'adapt-migrations';

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I copied this from an example file, but will correct and only use methods that are used in the migration script.

Copy link
Member

@oliverfoster oliverfoster Feb 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be able to use eslint inside migration scripts. It'll flag up red for stuff that isn't needed, or whitespace and indentation that's off.

image

migrations/v6.js Outdated
mutateContent('adapt-contrib-text - update _globals ariaRegion', async content => {
course = content.find(({ _type }) => _type === 'course');
setObjectPathValue(course, '_globals._components._text.ariaRegion', '');
delete course?._globals?._text;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this used for testing? Don't think we need to delete the globals here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the location of the ariaRegion in the v1 schema before it was updated, so it should be deleted to keep things correct.

migrations/v6.js Outdated
if (!isValid) throw new Error('_globals ariaRegion not updated');
return true;
});
updatePlugin('adapt-contrib-text - update to v6.1.3', {name: 'adapt-contrib-text', version: '6.1.3', framework: '>=5.19.1'})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
updatePlugin('adapt-contrib-text - update to v6.1.3', {name: 'adapt-contrib-text', version: '6.1.3', framework: '>=5.19.1'})
updatePlugin('adapt-contrib-text - update to v6.1.3', { name: 'adapt-contrib-text', version: '6.1.3', framework: '>=5.19.1' });

migrations/v6.js Outdated
* correct v1 schemas - legacy schema correct in v2.0.1
*/
describe('adapt-contrib-text - v4.2.0 to v6.1.3', async () => {
whereFromPlugin('adapt-contrib-text - from v4.2.0 to v6.1.3', { name: 'adapt-contrib-text', version: '<6.1.3'});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
whereFromPlugin('adapt-contrib-text - from v4.2.0 to v6.1.3', { name: 'adapt-contrib-text', version: '<6.1.3'});
whereFromPlugin('adapt-contrib-text - from v4.2.0 to v6.1.3', { name: 'adapt-contrib-text', version: '<6.1.3' });

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Needs Reviewing
Development

Successfully merging this pull request may close these issues.

Add migration scripts
3 participants