fix: update association value
This commit is contained in:
		
							parent
							
								
									f09991d932
								
							
						
					
					
						commit
						90846686ac
					
				@ -35,44 +35,6 @@ describe('database', () => {
 | 
			
		||||
    expect(imageCollection.fields.has('url2')).toBeTruthy();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  test('hasMany with inverse belongsTo relation', async () => {
 | 
			
		||||
    const db = mockDatabase();
 | 
			
		||||
    const UserCollection = db.collection({
 | 
			
		||||
      name: 'users',
 | 
			
		||||
      fields: [
 | 
			
		||||
        { type: 'string', name: 'name' },
 | 
			
		||||
        { type: 'hasMany', name: 'posts' },
 | 
			
		||||
      ],
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const PostCollection = db.collection({
 | 
			
		||||
      name: 'posts',
 | 
			
		||||
      fields: [{ type: 'string', name: 'title' }],
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    expect(UserCollection.model.associations.posts).toBeDefined();
 | 
			
		||||
    expect(PostCollection.model.associations.user).toBeDefined();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  test('belongsTo with inverse hasMany relation', async () => {
 | 
			
		||||
    const db = mockDatabase();
 | 
			
		||||
    const UserCollection = db.collection({
 | 
			
		||||
      name: 'users',
 | 
			
		||||
      fields: [{ type: 'string', name: 'name' }],
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const PostCollection = db.collection({
 | 
			
		||||
      name: 'posts',
 | 
			
		||||
      fields: [
 | 
			
		||||
        { type: 'string', name: 'title' },
 | 
			
		||||
        { type: 'belongsTo', name: 'user' },
 | 
			
		||||
      ],
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    expect(PostCollection.model.associations.user).toBeDefined();
 | 
			
		||||
    expect(UserCollection.model.associations.posts).toBeDefined();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  test('get collection', async () => {
 | 
			
		||||
    const db = mockDatabase();
 | 
			
		||||
    expect(db.getCollection('test')).toBeUndefined();
 | 
			
		||||
 | 
			
		||||
@ -17,6 +17,7 @@ describe('option parser', () => {
 | 
			
		||||
      fields: [
 | 
			
		||||
        { type: 'string', name: 'name' },
 | 
			
		||||
        { type: 'integer', name: 'age' },
 | 
			
		||||
        { type: 'hasMany', name: 'posts' },
 | 
			
		||||
      ],
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,10 @@
 | 
			
		||||
import { mockDatabase } from '../index';
 | 
			
		||||
import { BelongsToManyRepository } from '../../relation-repository/belongs-to-many-repository';
 | 
			
		||||
import Database from '../../database';
 | 
			
		||||
 | 
			
		||||
describe('belongs to many', () => {
 | 
			
		||||
  let db;
 | 
			
		||||
  let db: Database;
 | 
			
		||||
  let User;
 | 
			
		||||
  let Post;
 | 
			
		||||
  let Tag;
 | 
			
		||||
  let PostTag;
 | 
			
		||||
@ -14,11 +16,29 @@ describe('belongs to many', () => {
 | 
			
		||||
      fields: [{ type: 'string', name: 'tagged_at' }],
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    User = db.collection({
 | 
			
		||||
      name: 'users',
 | 
			
		||||
      fields: [
 | 
			
		||||
        {
 | 
			
		||||
          type: 'string',
 | 
			
		||||
          name: 'name',
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          type: 'hasMany',
 | 
			
		||||
          name: 'posts',
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    Post = db.collection({
 | 
			
		||||
      name: 'posts',
 | 
			
		||||
      fields: [
 | 
			
		||||
        { type: 'belongsToMany', name: 'tags', through: 'posts_tags' },
 | 
			
		||||
        { type: 'string', name: 'title' },
 | 
			
		||||
        {
 | 
			
		||||
          type: 'belongsTo',
 | 
			
		||||
          name: 'user',
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -248,6 +268,50 @@ describe('belongs to many', () => {
 | 
			
		||||
    expect(p2Tag.posts_tags.tagged_at).toBeNull();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  test('update association values', async () => {
 | 
			
		||||
    const u1 = await User.repository.create({
 | 
			
		||||
      values: {
 | 
			
		||||
        name: 'u1',
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const p1 = await Post.repository.create({
 | 
			
		||||
      values: {
 | 
			
		||||
        title: 'p1',
 | 
			
		||||
        tags: [{ name: 't1' }, { name: 't2' }],
 | 
			
		||||
        user: u1.id,
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const tag = await Tag.repository.findOne();
 | 
			
		||||
    const tagPostsRepository = new BelongsToManyRepository(Tag, 'posts', tag.id);
 | 
			
		||||
 | 
			
		||||
    await tagPostsRepository.update({
 | 
			
		||||
      values: {
 | 
			
		||||
        user: {
 | 
			
		||||
          id: u1.get('id'),
 | 
			
		||||
          name: 'u0',
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    await u1.reload();
 | 
			
		||||
    expect(u1.get('name')).toEqual('u1');
 | 
			
		||||
 | 
			
		||||
    await tagPostsRepository.update({
 | 
			
		||||
      values: {
 | 
			
		||||
        user: {
 | 
			
		||||
          id: u1.get('id'),
 | 
			
		||||
          name: 'u0',
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      updateAssociationValues: ['user'],
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    await u1.reload();
 | 
			
		||||
    expect(u1.get('name')).toEqual('u0');
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  test('add', async () => {
 | 
			
		||||
    let t1 = await Tag.repository.create({
 | 
			
		||||
      values: { name: 't1' },
 | 
			
		||||
 | 
			
		||||
@ -21,12 +21,16 @@ describe('repository.find', () => {
 | 
			
		||||
      name: 'posts',
 | 
			
		||||
      fields: [
 | 
			
		||||
        { type: 'string', name: 'name' },
 | 
			
		||||
        { type: 'belongsTo', name: 'user' },
 | 
			
		||||
        { type: 'hasMany', name: 'comments' },
 | 
			
		||||
      ],
 | 
			
		||||
    });
 | 
			
		||||
    Comment = db.collection({
 | 
			
		||||
      name: 'comments',
 | 
			
		||||
      fields: [{ type: 'string', name: 'name' }],
 | 
			
		||||
      fields: [
 | 
			
		||||
        { type: 'string', name: 'name' },
 | 
			
		||||
        { type: 'belongsTo', name: 'post' },
 | 
			
		||||
      ],
 | 
			
		||||
    });
 | 
			
		||||
    await db.sync();
 | 
			
		||||
    await User.repository.createMany({
 | 
			
		||||
 | 
			
		||||
@ -15,6 +15,7 @@ describe('repository find', () => {
 | 
			
		||||
      fields: [
 | 
			
		||||
        { type: 'string', name: 'name' },
 | 
			
		||||
        { type: 'integer', name: 'age' },
 | 
			
		||||
        { type: 'hasMany', name: 'posts' },
 | 
			
		||||
      ],
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -9,6 +9,8 @@ import {
 | 
			
		||||
  BelongsTo,
 | 
			
		||||
  BelongsToMany,
 | 
			
		||||
  HasMany,
 | 
			
		||||
  Transactionable,
 | 
			
		||||
  Hookable,
 | 
			
		||||
} from 'sequelize';
 | 
			
		||||
import { UpdateGuard } from './update-guard';
 | 
			
		||||
import { TransactionAble } from './repository';
 | 
			
		||||
@ -21,6 +23,10 @@ function isStringOrNumber(value: any) {
 | 
			
		||||
  return typeof value === 'string' || typeof value === 'number';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getKeysByPrefix(keys: string[], prefix: string) {
 | 
			
		||||
  return keys.filter((key) => key.startsWith(`${prefix}.`)).map((key) => key.substring(prefix.length + 1));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function modelAssociations(instance: Model) {
 | 
			
		||||
  return (<typeof Model>instance.constructor).associations;
 | 
			
		||||
}
 | 
			
		||||
@ -57,6 +63,11 @@ interface UpdateOptions extends TransactionAble {
 | 
			
		||||
  sourceModel?: Model;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface UpdateAssociationOptions extends Transactionable, Hookable {
 | 
			
		||||
  updateAssociationValues?: string[];
 | 
			
		||||
  sourceModel?: Model;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function updateModelByValues(instance: Model, values: UpdateValue, options?: UpdateOptions) {
 | 
			
		||||
  if (!options?.sanitized) {
 | 
			
		||||
    const guard = new UpdateGuard();
 | 
			
		||||
@ -105,7 +116,7 @@ export async function updateThroughTableValue(
 | 
			
		||||
 * @param values
 | 
			
		||||
 * @param options
 | 
			
		||||
 */
 | 
			
		||||
export async function updateAssociations(instance: Model, values: any, options: any = {}) {
 | 
			
		||||
export async function updateAssociations(instance: Model, values: any, options: UpdateAssociationOptions = {}) {
 | 
			
		||||
  // if no values set, return
 | 
			
		||||
  if (!values) {
 | 
			
		||||
    return;
 | 
			
		||||
@ -119,8 +130,10 @@ export async function updateAssociations(instance: Model, values: any, options:
 | 
			
		||||
    transaction = await instance.sequelize.transaction();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const keys = Object.keys(values);
 | 
			
		||||
 | 
			
		||||
  for (const key of Object.keys(modelAssociations(instance))) {
 | 
			
		||||
    if (values[key]) {
 | 
			
		||||
    if (keys.includes(key)) {
 | 
			
		||||
      await updateAssociation(instance, key, values[key], {
 | 
			
		||||
        ...options,
 | 
			
		||||
        transaction,
 | 
			
		||||
@ -159,7 +172,12 @@ export async function updateAssociations(instance: Model, values: any, options:
 | 
			
		||||
 * @param value
 | 
			
		||||
 * @param options
 | 
			
		||||
 */
 | 
			
		||||
export async function updateAssociation(instance: Model, key: string, value: any, options: any = {}) {
 | 
			
		||||
export async function updateAssociation(
 | 
			
		||||
  instance: Model,
 | 
			
		||||
  key: string,
 | 
			
		||||
  value: any,
 | 
			
		||||
  options: UpdateAssociationOptions = {},
 | 
			
		||||
) {
 | 
			
		||||
  const association = modelAssociationByKey(instance, key);
 | 
			
		||||
 | 
			
		||||
  if (!association) {
 | 
			
		||||
@ -183,7 +201,12 @@ export async function updateAssociation(instance: Model, key: string, value: any
 | 
			
		||||
 * @param value
 | 
			
		||||
 * @param options
 | 
			
		||||
 */
 | 
			
		||||
export async function updateSingleAssociation(model: Model, key: string, value: any, options: any = {}) {
 | 
			
		||||
export async function updateSingleAssociation(
 | 
			
		||||
  model: Model,
 | 
			
		||||
  key: string,
 | 
			
		||||
  value: any,
 | 
			
		||||
  options: UpdateAssociationOptions = {},
 | 
			
		||||
) {
 | 
			
		||||
  const association = <HasOne | BelongsTo>modelAssociationByKey(model, key);
 | 
			
		||||
 | 
			
		||||
  if (!association) {
 | 
			
		||||
@ -194,7 +217,8 @@ export async function updateSingleAssociation(model: Model, key: string, value:
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const { transaction = await model.sequelize.transaction() } = options;
 | 
			
		||||
  const { updateAssociationValues = [], transaction = await model.sequelize.transaction() } = options;
 | 
			
		||||
  const keys = getKeysByPrefix(updateAssociationValues, key);
 | 
			
		||||
 | 
			
		||||
  try {
 | 
			
		||||
    // set method of association
 | 
			
		||||
@ -220,6 +244,7 @@ export async function updateSingleAssociation(model: Model, key: string, value:
 | 
			
		||||
      }
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (value instanceof Model) {
 | 
			
		||||
      await model[setAccessor](value);
 | 
			
		||||
      model.setDataValue(key, value);
 | 
			
		||||
@ -248,9 +273,15 @@ export async function updateSingleAssociation(model: Model, key: string, value:
 | 
			
		||||
        },
 | 
			
		||||
        transaction,
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      if (instance) {
 | 
			
		||||
        await model[setAccessor](instance);
 | 
			
		||||
        await updateAssociations(instance, value, { transaction, ...options });
 | 
			
		||||
 | 
			
		||||
        if (updateAssociationValues.includes(key)) {
 | 
			
		||||
          await instance.update(value, { ...options, transaction });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await updateAssociations(instance, value, { ...options, transaction, updateAssociationValues: keys });
 | 
			
		||||
        model.setDataValue(key, instance);
 | 
			
		||||
        if (!options.transaction) {
 | 
			
		||||
          await transaction.commit();
 | 
			
		||||
@ -259,12 +290,8 @@ export async function updateSingleAssociation(model: Model, key: string, value:
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (value[dataKey] === null) {
 | 
			
		||||
      return await removeAssociation();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const instance = await model[createAccessor](value, { transaction });
 | 
			
		||||
    await updateAssociations(instance, value, { transaction, ...options });
 | 
			
		||||
    await updateAssociations(instance, value, { ...options, transaction, updateAssociationValues: keys });
 | 
			
		||||
    model.setDataValue(key, instance);
 | 
			
		||||
    // @ts-ignore
 | 
			
		||||
    if (association.targetKey) {
 | 
			
		||||
@ -288,7 +315,12 @@ export async function updateSingleAssociation(model: Model, key: string, value:
 | 
			
		||||
 * @param value
 | 
			
		||||
 * @param options
 | 
			
		||||
 */
 | 
			
		||||
export async function updateMultipleAssociation(model: Model, key: string, value: any, options: any = {}) {
 | 
			
		||||
export async function updateMultipleAssociation(
 | 
			
		||||
  model: Model,
 | 
			
		||||
  key: string,
 | 
			
		||||
  value: any,
 | 
			
		||||
  options: UpdateAssociationOptions = {},
 | 
			
		||||
) {
 | 
			
		||||
  const association = <BelongsToMany | HasMany>modelAssociationByKey(model, key);
 | 
			
		||||
 | 
			
		||||
  if (!association) {
 | 
			
		||||
@ -299,7 +331,8 @@ export async function updateMultipleAssociation(model: Model, key: string, value
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const { transaction = await model.sequelize.transaction() } = options;
 | 
			
		||||
  const { updateAssociationValues = [], transaction = await model.sequelize.transaction() } = options;
 | 
			
		||||
  const keys = getKeysByPrefix(updateAssociationValues, key);
 | 
			
		||||
 | 
			
		||||
  try {
 | 
			
		||||
    const setAccessor = association.accessors.set;
 | 
			
		||||
@ -359,7 +392,7 @@ export async function updateMultipleAssociation(model: Model, key: string, value
 | 
			
		||||
      if (isUndefinedOrNull(item[pk])) {
 | 
			
		||||
        // create new record
 | 
			
		||||
        const instance = await model[createAccessor](item, accessorOptions);
 | 
			
		||||
        await updateAssociations(instance, item, { transaction, ...options });
 | 
			
		||||
        await updateAssociations(instance, item, { ...options, transaction, updateAssociationValues: keys });
 | 
			
		||||
        list3.push(instance);
 | 
			
		||||
      } else {
 | 
			
		||||
        // set & update record
 | 
			
		||||
@ -369,7 +402,10 @@ export async function updateMultipleAssociation(model: Model, key: string, value
 | 
			
		||||
        const addAccessor = association.accessors.add;
 | 
			
		||||
 | 
			
		||||
        await model[addAccessor](item[pk], accessorOptions);
 | 
			
		||||
        await updateAssociations(instance, item, { transaction, ...options });
 | 
			
		||||
        if (updateAssociationValues.includes(key)) {
 | 
			
		||||
          await instance.update(item, { ...options, transaction });
 | 
			
		||||
        }
 | 
			
		||||
        await updateAssociations(instance, item, { ...options, transaction, updateAssociationValues: keys });
 | 
			
		||||
        list3.push(instance);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user