Giter Site home page Giter Site logo

emberexperts / ember-custom-actions Goto Github PK

View Code? Open in Web Editor NEW
74.0 2.0 25.0 12.5 MB

Custom API actions for Ember applications

Home Page: http://emberexperts.github.io/ember-custom-actions/

License: MIT License

JavaScript 74.35% HTML 6.10% Handlebars 14.59% Sass 4.97%
ember ember-addon api jsonapi javascript

ember-custom-actions's Introduction

Ember Custom Actions Logo

Ember Custom Actions is a package for defining custom API actions, dedicated for Ember 2.16 (and higher) applications.

Getting started

Demo

Before you will start with documentation check our demo app: Ember-Custom-Actions Website

Installation

ember install ember-custom-actions

Documentation

Model actions

To define custom action like: posts/1/publish you can use modelAction(path, options) method with arguments:

  • path - url of the action scoped to our api (in our case it's publish)
  • options - optional parameter which will overwrite the configuration options
import Model from 'ember-data/model';
import { modelAction } from 'ember-custom-actions';

export default Model.extend({
  publish: modelAction('publish', { pushToStore: false }),
});

Usage

let user = this.get('currentUser');
let postToPublish = this.get('store').findRecord('post', 1);
let payload = { publisher: user };

postToPublish.publish(payload, /*{ custom options }*/).then((status) => {
  alert(`Post has been: ${status}`)
}).catch((error) => {
  console.log('Here are your serialized model errors', error.serializedErrors);
});

Resource actions

To a define custom action like: posts/favorites you can use resourceAction(actionId/path, options) method with arguments:

  • path - url of the action scoped to our api (in our case it's favorites)
  • options - optional parameter which will overwrite the configuration options
import Model from 'ember-data/model';
import { resourceAction } from 'ember-custom-actions';

export default Model.extend({
  favorites: resourceAction('favorites', { method: 'GET' }),
});

Usage

let user = this.get('currentUser');
let emptyPost = this.get('store').createRecord('post');
let payload = { user };

emptyPost.favorites(payload, /*{ custom options }*/).then((favoritesPosts) => {
  console.log(favoritesPosts);
}).finally(()=>{
  emptyPost.deleteRecord();
});

Custom actions

To define customAction and customize it by using ember-data flow, adapters and serializer you can use customAction(actionId, options) method with arguments:

  • actionId - id of the action which can be handled later on in adpaters and serializers
  • options - optional parameter which will overwrite the configuration options

If you want to customize your request in your adapter please, implement our adapter mixin:

import JSONAPIAdapter from 'ember-data/adapters/json-api';
import { AdapterMixin } from 'ember-custom-actions';

export default JSONAPIAdapter.extend(AdapterMixin);

Now you can customize following methods in the adpater:

urlForCustomAction

You can define your custom path for every customAction by adding a conditional:

export default JSONAPIAdapter.extend(AdapterMixin, {
  urlForCustomAction(modelName, id, snapshot, actionId, queryParams) {
    if (actionId === 'myPublishAction') {
      return 'https://my-custom-api.com/publish'
    }

    return this._super(...arguments);
  }
});

If you would like to build custom modelAction you can do it by:

import { AdapterMixin } from 'ember-custom-actions';

export default JSONAPIAdapter.extend(AdapterMixin, {
  urlForCustomAction(modelName, id, snapshot, actionId, queryParams) {
    if (requestType === 'myPublishAction') {
      return `${this._buildURL(modelName, id)}/publish`;
    }

    return this._super(...arguments);
  }
});

methodForCustomAction

You can define your custom method for every customAction by adding a conditional:

import { AdapterMixin } from 'ember-custom-actions';

export default JSONAPIAdapter.extend(AdapterMixin, {
  methodForCustomAction(params) {
    if (params.actionId === 'myPublishAction') {
      return 'PUT';
    }

    return this._super(...arguments);
  }
});

headersForCustomAction

You can define your custom headers for every customAction by adding a conditional:

import { AdapterMixin } from 'ember-custom-actions';

export default JSONAPIAdapter.extend(AdapterMixin, {
  headersForCustomAction(params) {
    if (params.actionId === 'myPublishAction') {
      return {
        'Authorization-For-Custom-Action': 'mySuperToken123'
      };
    }

    return this._super(...arguments);
  }
});

dataForCustomAction

You can define your custom data for every customAction by adding a conditional:

import { AdapterMixin } from 'ember-custom-actions';

export default JSONAPIAdapter.extend(AdapterMixin, {
  dataForCustomAction(params) {
    if (params.actionId === 'myPublishAction') {
      return {
        myParam: 'send it to the server'
      };
    }

    return this._super(...arguments);
  }
});

params contains following data: data, actionId, modelId, model

Configuration

You can define your custom options in your config/environment.js file

module.exports = function(environment) {
  var ENV = {
    'emberCustomActions': {
      method: 'POST',
      data: {},
      headers: {},
      queryParams: {},
      ajaxOptions: {},
      adapterOptions: {},
      pushToStore: false,
      responseType: null,
      normalizeOperation: ''
    },
  };

  return ENV;
}

method

Default method of the request (GET, PUT, POST, DELETE, etc..)

headers

An object {} of custom headers. Eg:

{
  'my-custom-auth': 'mySuperToken123'
}

ajaxOptions

Your own ajax options. ** USE ONLY IF YOU KNOW WHAT YOU ARE DOING! ** Those properties will be overwritten by ECU.

pushToStore

If you want to push the received data to the store, set this option to true

normalizeOperation

You can define how your outgoing data should be serialized


Exemplary data:
```js
{
  firstParam: 'My Name',
  colors: { rubyRed: 1, blueFish: 3 }
}

After using a dasherize transformer our request data will turn into:

{
  first-param: 'My Name',
  colors: { ruby-red: 1, blue-fish: 3 }
}

It's great for API with request data format restrictions

Available transformers:

  • camelize
  • capitalize
  • classify
  • dasherize
  • decamelize
  • underscore

adapterOptions

Pass custom adapter options to handle them in urlForCustomAction in case of using customAction. Required usage of mixin: AdpaterMixin

responseType

You can easily observe the returned model by changing responseType to array or object according to what type of data your server will return.

When array:

model.customAction({}, { responseType: 'array' }) // returns DS.PromiseArray

When object:

model.customAction({}, { responseType: 'object' }) // returns DS.PromiseObject

When null (default):

model.customAction({}, { responseType: null }) // returns Promise

null is useful if you don't care about the response or just want to use then on the promise without using binding or display it in the template.

queryParams

You can pass a query params for a request by passing an {} with properties, eg: { include: 'owner' } ** Remember: Query params are not normalized! You have to pass it in the correct format. **

Development

Installation

  • git clone https://github.com/Exelord/ember-custom-actions.git
  • cd ember-custom-actions
  • npm install

Linting

  • npm run lint:hbs
  • npm run lint:js
  • npm run lint:js -- --fix

Running tests

  • ember test โ€“ Runs the test suite on the current Ember version
  • ember test --server โ€“ Runs the test suite in "watch mode"
  • ember try:each โ€“ Runs the test suite against multiple Ember versions

Running the dummy application

For more information on using ember-cli, visit https://ember-cli.com/.

Thanks

Big thanks to Mike North and his Project for the initial concept.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/exelord/ember-custom-actions. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.

License

This version of the package is available as open source under the terms of the MIT License.

ember-custom-actions's People

Contributors

acorncom avatar dcyriller avatar dependabot[bot] avatar ember-tomster avatar exelord avatar feanor07 avatar gitter-badger avatar krzyrok avatar louim avatar oakho avatar sandstrom avatar wozny1989 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

ember-custom-actions's Issues

Pass payload through serializer?

I have a non-REST endpoint users/invite and I'm trying to figure how to use this library to send a payload to that endpoint using JSONAPI format. My Application serializer uses JSONAPISerializer from ember-custom-actions already (I'm using this library in a couple other places already).

I'd like user.invite() to behave the same as user.save() in the sense that the User model is serialized (JSONAPI format) and sent as params to the endpoint.

On my User model, I have the following property:

invite: resourceAction("invite", { method: "POST" })

and in my invite action, I'm doing this:

invite(user) {
  user.invite(user.toJSON()).then(...)
}

The problem is that the payload doesn't pass through the ApplicationSerializer. I feel like I'm missing something obvious here!

Way to use in form of decorator!

I am trying to upgrade project to use classes based models. I was wondering if there is a way to use this addon in form of decorator?

urlForCustomAction not working.

Hi. your addon is really cool.

I configured it as you explain in your docs. I am using version 2.1.0.

In my adapter, I am using some customised url but it is not taking affect. I am not sure why.
This is the url that its make api/v1/users/resetPassword, when I just want api/v1/resetPassword

This is my adapter code. It is already inheriting RESTAdapter.

import ApplicationAdapter from './application'; 

import { AdapterMixin } from 'ember-custom-actions';

export default ApplicationAdapter.extend(AdapterMixin,{

urlForCustomAction(modelName, id, snapshot, actionId, queryParams) {

return 'https://domain.com/resetPassword';

}

});

Any help will be really appreciated.

Thanks in advance.

Resource actions .then() returning undefined

I have a resourceAction defined on one of my models called 'workflow' like so:

default: resourceAction('default', { pushToStore: true, type: 'POST', promiseType: null }),

Essentially in our API we have an endpoint POST /workflows/default which when posted to with minimal required values will create a new 'workflow' based on some defaults they have saved.

However when trying the following...

let emptyWorkflow = this.get('store').createRecord('workflow');

emptyWorkflow.default(payload).then((workflow) => {
    console.log('default wf created:', workflow); // << 'workflow' is undefined here!
}).finally(()=>{
    emptyWorkflow.deleteRecord();
});

...nothing seems to be passed through to the .then the workflow param is undefined.

The backend does create the item though and responds with the attributes. I can also see via Ember inspector that the new 'workflow' is added to the store, however I'd like to get it's ID at the point of the console.log in the above example so I can transition to it.

I also tried setting the option pushToStore: false, then I get an object containing the backend API's response payload. Is it possible to get the actual Ember model object for the newly created item passed through to the .then()?

No compatible version found 1.6.0 on npm install

npm ERR! Linux 4.4.0-59-generic
npm ERR! argv "/usr/bin/nodejs" "/usr/bin/npm" "install"
npm ERR! node v6.11.0
npm ERR! npm  v3.10.10
npm ERR! code ETARGET

npm ERR! notarget No compatible version found: [email protected]
npm ERR! notarget Valid install targets:
npm ERR! notarget 1.4.0, 1.3.0, 1.2.2, 1.2.0, 1.1.1, 1.1.0, 1.0.0
npm ERR! notarget 
npm ERR! notarget This is most likely not a problem with npm itself.
npm ERR! notarget In most cases you or one of your dependencies are requesting
npm ERR! notarget a package version that doesn't exist.
npm ERR! notarget 
npm ERR! notarget It was specified as a dependency of 'sisarq-frontend'
npm ERR! notarget 

Suppport for adapterOptions on modelActions

I use a dedicated endpoint /api/me for managing the current user as setup in ember-simple-auth/guides/managing-current-user. Updating attributes on the current user model is done by: currentUser.save({ adapterOptions: { me: true } }). Overriding the urlFor* methods on ApplicationAdapter replaces the id with me before making the request.

Is it possible to add support for adapterOptions when calling a modelAction? For example:

const me = get(this, 'currentUser.user');
me.unreadNotificationCount({ adapterOptions: { me: true } }).then((response) => ...);

As a workaround, I create an empty user model with id = me (similiar to example for Resource actions):

const me = get(this, 'store').createRecord('user', { id: 'me' });
me.unreadNotificationCount({ adapterOptions: { me: true } }).then((response) => ...);

Include params

Other than putting them into the URL, is there a way to add include params?

  findWookies: resourceAction('find-em?include=wookie,tart', {
    type: 'POST',
    pushToStore: true
  }),

Warning: cjs already defined

The following warning shows up when my app builds:

WARNING: Addon "ember-custom-actions" is defining a transform name: cjs that is already being defined. Using transform from addon: "ember-custom-actions".

Any idea what this means?

I'm using Ember 2.18 and ember-custom-actions 3.0.0.

Thanks

[Feature request] Make it possible to add an extra 'callback' function to custom actions

I think it would be nice if you could pass extra 'callback' functions to the modelAction function.

This makes it possible to optimistically change the state of the model to what is the expected result from the server which improves usability.

What it could look like:

export default Model.extend({
  isFavorited: attr('boolean'),

  favorite: modelAction('favorite', {}, function () {
    this.set('isFavorited', true);
  }),
});

At the moment this is possible by writing a wrapper function, which calls the custom action, but I think it would be an improvement if this was included in this addon.

Let me know what you think!

pushToStore Array vs Object Mismatch

Code Block I'm Referencing

In the _onSuccess handler, when pushToStore is true, this._validResponse(response) ensures the response is an object with > 0 keys. However later on when this.get('serializer').normalizeArrayResponse is called with response as the payload.

normalizeArrayResponse's payload is expected to be an array, when previously is was required to be an object. With the standard JsonSerializer it errors with an object is provided, and does not go into this block when an array is provided as the response.

Is normalizeArrayResponse intended to be normalizeSingleResponse? Which would make more sense to me when operating on a single record, I would think custom actions would usually return an updated version of that object.

Maybe it should check the type and normalize as a SingleResponse or an ArrayResponse based on type, so custom search actions can still return an array.

Passing completely empty payload in custom action

Hi, my server expects payloads for actions to be form parameters (application/x-www-form-urlencoded) and not json. Alternatively it will accept an empty payload and the parameters can be passed as query parameters instead. I'm trying to set an empty payload both when calling the custom action, and in the dataForCustomAction method. However ember-custom-actions is turning this empty string payload into an empty object which ends up sending 2 characters '{}' to the server. This is enough for the server to not accept the query parameters i'm passing.

Do you have any work arounds for this to allow me to send a completely empty payload (or alternatively to send form parameters as the payload rather than json)

Thanks!

Warning due to ember-cli-babel below 7.26.6

We're getting an error message due to this project using an older version of ember-cli-babel.

Possible fix

Don't know how many side-effects there are to bumping the babel dependency, but v7 doesn't seem to contain too many breaking changes (https://github.com/babel/ember-cli-babel/blob/master/CHANGELOG.md#v700-2018-08-28).

However, there may be inter-dependencies with other packages too, so not sure.

Deprecation Message

Usage of the Ember Global is deprecated. You should import the Ember module or the specific API instead.

See https://deprecations.emberjs.com/v3.x/#toc_ember-global for details.

Usages of the Ember Global may be caused by an outdated ember-cli-babel dependency. The following steps may help:

* Upgrade the following addons to the latest version:
  * ember-custom-actions

### Important ###

In order to avoid repeatedly showing the same deprecation messages, no further deprecation messages will be shown for usages of the Ember Global until ember-cli-babel is upgraded to v7.26.6 or above.

To see all instances of this deprecation message, set the `EMBER_GLOBAL_DEPRECATIONS` environment variable to "all", e.g. `EMBER_GLOBAL_DEPRECATIONS=all ember test`.

### Details ###

Prior to v7.26.6, ember-cli-babel sometimes transpiled imports into the equivalent Ember Global API, potentially triggering this deprecation message indirectly, even when you did not observe these deprecated usages in your code.

The following outdated versions are found in your project:

* [email protected], currently used by:
  * [email protected]
    * Depends on [email protected]

Could not find module `ember-data/-private/debug` imported from `ember-custom-actions/serializers/rest`

I'm running into an issue where this error is being raised:

Could not find module `ember-data/-private/debug` imported from `ember-custom-actions/serializers/rest`

I think it relates to emberjs/data#5021 but it still seems like a bug with this library. Looks like a private API is being used, which has been moved in recent versions of Ember Data?

This issue exists at least in Ember Data 2.14.3.

Assertion Failed: You must provide a path for model action!

I'd like to use ember-custom-actions to implement a softDelete() method on a model that issues a DELETE request to the resource endpoint. For example, if the resource is tasks and the ID is 2, the URL would be api/tasks/2.

I've implemented a modelAction, however I haven't been able to configure it as I'd expected, and I'm running into the error Assertion Failed: You must provide a path for model action! because the path is empty ( I don't want the URL to be api/tasks/2/soft-delete, for example).

Here's the model config:

softDelete: modelAction("", { method: "DELETE" })

I'm also observing that the method isn't respected, as a PUT request is actually being issued when I test the path as /, for example.

Support for adapters layer

In my code I forced to send some headers with the custom actions. It ends up being a code like this:

const body = this.get("model.foo")
const etag = this.get("model.etag");
const ajaxOptions = {
  headers: {
    "If-Match": version
  }
};

this.get("model").barAction(body, {ajaxOptions})

In quite many places.
I believe this code should live in adapter of proper model, and would therefore like to suggest a new feature: adapter integration.

Custom action should look, by default, look for headers in models adapter. So having a code like this in model:

import ApplicationModel from "./application-model";
import { modelAction } from "ember-custom-actions";

export default ApplicationModel.extend({
  barAction: modelAction("bar-action"),
});

and adapter like this:

import ApplicationAdapter from "./application";

export default ApplicationAdapter.extend({
  headersForRequest(params) {
    if(params.requestType === "bar-action") {
      const etag = params.snapshot.attr("etag");
      return {
        "If-Match": etag
      };
    } else {
      return this._super(params);
    }
  },
});

It would be enough in actual calling of the action to do:

const body = this.get("model.foo")
this.get("model").barAction(body)

If this idea is accepted I am more than happy to implement it, it would cleanup my codebase nicely.

I also have other issues of the same nature: ability to specify url in adapter for a custom action, and ability to specify a body in serializer in the similiar manner. I'm happy to create new issues for them when this one is settled, or right away.

EDIT

After some more time spend thinking about it, maybe better approach would be to introduce 3 new methods specific for custom actions: urlForCustomAction, headersForCustomAction and methodForCustomAction ?

Code would look for them in adapter, and if they are there, their result would be used.

Errors not being added to model

If the record fails validation on the server and returns a 422 with JSON-API compliant errors, these errors do not get set on the model. I have pushToStore turned on.

Is this expected behavior? Is there a good way to set the model errors manually?

Remove computed-property.override deprecation

I see this deprecation since Ember 3.9.0:

DEPRECATION: The <(unknown):ember2859>#options computed property was just overriden. This removes the computed property and replaces it with a plain value, and has been deprecated. If you want this behavior, consider defining a setter which does it manually. [deprecation id: computed-property.override] See https://emberjs.com/deprecations/v3.x#toc_computed-property-override for more details.

I believe the code responsible for that is:

https://github.com/Exelord/ember-custom-actions/blob/fa509a8ab6106624b89363d23eb2895eb6793f64/addon/actions/custom.js#L8-L14

Where you override options computed property:

https://github.com/Exelord/ember-custom-actions/blob/fa509a8ab6106624b89363d23eb2895eb6793f64/addon/actions/action.js#L44-L46

Solution:
I think you should set options value ({}) in the init hook. It seems like computed prop was used here to avoid state leaking.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.