tommybananas / finale

Create flexible REST endpoints and controllers from Sequelize models in your Express app
187 stars 36 forks source link

multiple scope support #35

Closed hiradimir closed 5 years ago

hiradimir commented 5 years ago

feature

I want to use multiple scope that is supported by sequelize. ?scope=scope1,scope2...&and-more-query

you can transform above request this sequelize code. sequelize.models['name'].scope('scope1').scope('scope2')

workaround.

now I use milestone.


const multipleScopeMilestone = {
  list: {
    fetch: {
      before: function (req, res, context) {
        if (req.query.scope) {
          this.model = this.model.scope('defaultScope').scope(req.query.scope.split(','));
          delete req.query.scope;
        }
        return context.continue;
      }
    }
  }
}
frankisans commented 5 years ago

Passing multiple scopes in query strings works:

Given two scope definitions:

@Scopes({
  notAssigned: {
    where: {
      id: {
        [Op.gt]: 9999
      }
    }
  },
  inZonesFixed: {
    where: {
      resourceId: {
        [Op.in]: [2,3]
      }
    }
  }
})

This url generate the right query:

http://localhost:8081/sat/scheduler/?scope=notAssigned&scope=inZonesFixed&offset=0&count=100

with the filter conditions of both scopes:

SELECT [ID] AS [id] FROM [appSatScheduler] AS [SatScheduler] 
WHERE [SatScheduler].[ID] > 9999 
AND [SatScheduler].[RESOURCEID] IN (2, 3) 
ORDER BY [SatScheduler].[ID] OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY;
hiradimir commented 5 years ago

@frankisans thanks for workaround.

I got oversights. I think your workaround is simple and excellent.

frankisans commented 5 years ago

@hiradimir to add a little more information. It is possible to invoke scopes which are functions in the following way:

Given this two scopes:

@Scopes({
  notAssigned: {
    where: {id: {[Op.gt]: 9999}}
  },
  inZones: (zones: number[]) => ({
    where: {resourceId: {[Op.in]: zones}}
  })
})

It is possible to call the second scope passing and object { method: ['inZones', [3,4,5] ]}. So we can use the following url to generate the query

http://localhost:8081/sat/scheduler/?scope[0]=notAssigned&scope[1][method][0]=inZones&scope[1][method][1]=3&scope[1][method][1]=4&&scope[1][method][1]=5
SELECT [ID] AS [id] FROM [appSatScheduler] AS [SatScheduler] 
WHERE [SatScheduler].[ID] > 9999 
AND [SatScheduler].[RESOURCEID] IN (N'3', N'4', N'5') 
ORDER BY [SatScheduler].[ID] OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY;
hiradimir commented 5 years ago

@frankisans Awesome! I already had gave up the feature!

I think the url is little bit dirty. But, useful for computers.

DenisFerrero commented 3 years ago

Hi, I know it's kinda late but for a project I use a lot of scopes and I developed this function to parse the scopes. You can find it on my Github Gist or written below

/*
 * Parse given array of object into Sequelize scope
 * Useful for create link with method oriented scopes
 * 
 * @param {Array[Object|String]|String|Object = []} scopes - Scopes to parse
 * @returns {String} - Parse scopes into a string
*/
function parseScope(scopes = []) {
    let stringBasedScope = '';
    if(!Array.isArray(scopes))
        scopes = [scopes];

    scopes.forEach((scope, scope_index) => {
        // Operate as array
        if(scope.method && Array.isArray(scope.method)) {
            scope.method.forEach((value, index) => {
                // Array support
                if(Array.isArray(value)) {
                    value.forEach(sub_value => {
                        stringBasedScope += `scope[${scope_index}][method][${index}]=${sub_value}&`;
                    });
                // Normal value
                } else {
                    stringBasedScope += `scope[${scope_index}][method][${index}]=${value}&`;
                }
            });
        // Operate as string
        } else if (typeof scope === 'string') {
            stringBasedScope += `scope[${scope_index}]=${scope}&`;
        }
    });

    return stringBasedScope;
}