Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 7 years ago.
Improve this question
Question
What is the best practice to avoid naming clashes in Coffeescript, especially given the context of nested scopes and Ajax callbacks?
My Problem
I'm experiencing a name collision issue, where my naming convention dictates that the name for data in Ajax callbacks is the same as another object in my scope.
In the following code, I'm putting all my functions on the object notifications, but the data coming out of my Ajax GET requests is being named notifications. The result is obviously causing errors:
# Handles initial get request.
notifications.init = ->
$.ajax 'notifications',
type: 'GET'
dataType: 'json'
error: (jqXHR, textStatus, errorThrown) ->
alert textStatus
success: (notifications, textStatus, jqXHR) ->
if notifications?
filteredNotifications = notifications.filteredNotifications notifications
notifications.behavior notifications
# Triggers the notifications
notifications.behavior = (filteredNotifications) ->
if filteredNotifications?
$('#counter').html filteredNotifications.length
if parseInt($('#counter').html()) > 0
$('#counter').css
'background': 'black'
# Removes notifications sent by the current user, copies for the other user,
# and notifications marked as observed.
notifications.filteredNotifications = (notifications) ->
filteredNotifications = filteredNotifications.filter((notification) ->
notification.recipients.username is $username() and
notification.copy_for_user_id is $id() and
notification.observed is false
)
return filteredNotifications
Considerations
I've considered various abbreviations of notifications for the data object in the Ajax callbacks, but it decreases readability. Naming the parent object differently doesn't seem appropriate either.
Here's my pitch. As you've said, this subject is very subjective, but I've tried to keep this pretty standard.
Two major points:
Abstracting logic away from namespaces is good practice, and results in less symbols to clash with. Coffeescript lets you instantiate anonymous classes, which helps a lot with this.
I exclusively use the identifier data as the name of data objects in JSON callbacks. This reduces confusion for anyone reading a callback, and helps to eliminate clashes like the one you've encountered.
notifications = new class
init: -> $.ajax 'notifications',
type: 'GET'
dataType: 'json'
error: (jqXHR, textStatus, errorThrown) ->
alert textStatus
success: (data, textStatus, jqXHR) =>
if data?
#behavior #filter data
# Triggers the notifications
behavior: (notifications) ->
if notifications?
$('#counter').html notifications.length
if notifications.length?
$('#counter').css
'background': 'black'
return
# Removes notifications sent by the current user, copies for the other user,
# and notifications marked as observed.
filter: (notifications) ->
return notifications.filter (notification) ->
notification.recipients.username is $username() and
notification.copy_for_user_id is $id() and
notification.observed is false
notifications.init()
I've made some other minor changes, and I took the liberty of making a logic change in behavior, which may not be perfect for your situation, but you should definitely revise the logic there anyway. It's unwise to use the DOM to store values you need in application logic.
Important to note is the use of the "fat arrow" (=>) in the success callback. It's required so you can bind the function to the correct context and have #behavior and #filter resolve correctly.
Related
I'm building a PWA application, and I have used workbox registerroute for a few api endpoints, as well as an explicit service worker fetch event listener. During the debugging on some caching issues, I've noticed that these two seems to interfere with each other. Specifically sometimes the fetch handler is not triggered - which causes me trouble on debugging - I'm assuming this is due to the registerroute caching policy I have set via workbox.
My question is that, can I only pick one or the other, instead of having both fetch handler and registerroute? In my case, I needed fetch handler to deal with some advanced caching related to POST requests. So I think if I can only pick one, I'll have to stick with the fetch handler.
First, here's some background information about what happens when there's multiple fetch event handlers in the active service worker.
With that background info in mind, there are a few approaches for accomplishing what you're describing.
Option 1a: Register your own fetch event handler first
As long as you register your own fetch handler first, before any calls to Workbox's registerRoute(), it's guaranteed to have the "first shot" at responding the incoming fetch event.
The thing to keep in mind is that your own fetch handler needs to make a synchronous decision about whether or not to call event.respondWith(), and when you do call event.respondWith(), then Workbox's routes will not get used to respond to a given request.
So, you could do the following:
self.addEventListener('fetch', (event) => {
// Alternatively, check event.request.headers,
// or some other synchronous criteria.
if (event.request.url.endsWith('.json')) {
event.respondWith(customResponseLogic(event));
}
});
// Then, include any Workbox-specific routes you want.
registerRoute(
({request}) => request.destination === 'image',
new CacheFirst()
);
// The default handler will only apply if your own
// fetch handler didn't respond.
registerDefaultHandler(new StaleWhileRevalidate());
Option 1b: Ensure Workbox routes won't match
This is similar to 1a, but the main thing is to make sure that you don't have a "catch-all" route that will match all requests, and that you don't use registerDefaultHandler().
Assuming your Workbox routes just match a specific set of well-defined criteria, and don't match any of the requests that you want to respond to in your own handler, it shouldn't matter how you order them:
// Because this will only match image requests, it doesn't
// matter if it's listed first.
registerRoute(
({request}) => request.destination === 'image',
new CacheFirst()
);
self.addEventListener('fetch', (event) => {
// Alternatively, check event.request.headers,
// or some other synchronous criteria.
if (event.request.url.endsWith('.json')) {
event.respondWith(customResponseLogic(event));
}
});
(What's going on "under the hood" is that if there isn't a Route whose synchronous matchHandler returns a truthy value, Workbox's Router won't call event.respondWith().)
Option 2: Use custom handler logic
It should be viable to use Workbox to handle all your routing, and run your custom response generation code in either a handlerCallback (more straightforward) or a custom subclass of the Strategy base class (more reusable, but overkill for simple use cases).
The one thing to keep in mind is that if you're dealing with POST requests, you need to explicitly tell registerRoute() to respond to them, by passing in 'POST' as the (optional) third parameter.
Here's an example of how you could do this, assuming as before that you custom logic is defined in a customResponseLogic() function:
registerRoute(
({request}) => request.destination === 'image',
new CacheFirst()
);
registerRoute(
// Swap this out for whatever criteria you need.
({url}) => url.pathname.endsWith('.json'),
// As before, this assumes that customResponseLogic()
// takes a FetchEvent and returns a Promise for a Response.
({event}) => customResponseLogic(event),
// Make sure you include 'POST' here!
'POST'
);
This seems a very naive set of questions, but I really couldn't find it.
Question 1) I am trying to implement IPN for PayPal. I want to enter The listener URL, but I couldn't find where. I've searched the internet and all the responses are old. Where could I put it?
Question 2) I am using the following code and I want to pass custom data so that when IPN sends me back the transaction message, It will have that custom data in it. I need to pass 2 custom data. How is this possible for the following code? I really couldn't find this one in the docs too.
<template>
<div id="paypal-button-container"></div>
</template>
<script>
export default {
props: ["amount"],
mounted() {
let self = this;
paypal
.Buttons({
createOrder: function(data, actions) {
// Set up the transaction
return actions.order.create({
purchase_units: [
{
amount: {
value: self.amount,
}
}
],
});
},
onApprove: function(data, actions) {
// Capture the funds from the transaction
return actions.order.capture().then(function(details) {
// Show a success message to your buyer
console.log(details);
self.$emit("paypalPaySuccess", {
order_id: details.id,
})
alert("Transaction completed by " + details.payer.name.given_name);
}).catch((err)=>{
})
}
})
.render("#paypal-button-container");
}
};
</script>
Addition of some questions
I am using all the events in dashboard.
I need to add some more questions, because I can't find the information I need.
Question 3) In client-side, I make action.orders.capture().then . which means that I capture funds immediatelly. When webhooks come to my back-end, there're 2 webhooks that come for single payment . (PAYMENT.CAPTURE.PENDING and CHECKOUT.ORDER.APPROVED) . a) why doesn't PAYMENT.CAPTURE.COMPLETED arrive as webhook event? b) what if I want to get DENIED events? If PAYMENT.CAPTURE.COMPLETED doesn't arrive, PAYMENT.CAPTURE.DENIED won't arrive too.
Question 4) As I said, there're 2 webhook events appearing for single payment on my back-end. The first one has id in it. Let's say I store this in my database as id and status(PENDING). Then another event came which is CHECKOUT.ORDER.APPROVED. Now, this one has different id then the previous one. So I can't really go back to database and update the status as this has different id. What should I do? One thing I noted is that the second came event has also another id (the previous came event) somewhere in captured object. is this what i should use it? which one is the final transaction_id?
Question 5) Turns out invoice_id should be unique each time. So I wanted to pass user_id but i guess i also should generate random string and append it to user_id something like this: invoice_id: '7,randomstring'. right?
Question 6) Same event sometimes comes twice. Why is that? Should I return status 200 in order for that same event not to come again? When will it be good to return that status? I guess after I update the database, right?
Q1: IPN is deprecated. You should be using webhooks, such as CHECKOUT.ORDER.APPROVED
Q2: There does not appear to be any specific way to pass custom data.
Option 1:
What I ended up doing was using the invoice_id field in purchase_units. I tested and found that I could pass a reasonably long string of characters and they did not have to be unique. It seems a bit of a hack but it works.
When you implement the CHECKOUT.ORDER.APPROVED webhook, you can then parse the data in purchase_unit invoice_id.
Option 2:
Another idea I considered which doesn't need to hack the invoice_id, is to send data to the server using the details from the capture event in the javascript, store that custom information, along with the order id in the database, and then when the webhook occurs, cross-match the order id to the saved one to retrieve the information.
I prefer option 1, but your needs may vary.
It's pretty indicative though of how poorly designed the whole PayPal API is. There are huge gaps in the implementation, very little documentation, and zero support.
I had to implement Stripe as well. The entire implementation took me 3 days. The same PayPal implementation took me over 3 weeks! The lack of documentation, having to constantly just try things out, and the inconsistency of the sandbox implementation were real time killers.
I'm looking for a pattern to implement in my application that would allow me to cleanly have my application pull information from several http requests, where each request needs to run sequentially, one after the other, and feed information from the former request to the next request. I have worked through many of the solutions in this question, but many of the answers are for old versions of Swift, there are conflicting solutions, there are non-working solutions, and none address getting data from one request to the next request. Also many of the ideas don't extend to more than about two requests.
The reason for having several requests that do not overlap is that data from earlier requests feed into the next request. Or it could be that depending on the data, it might make sense to skip some http requests.
I have tried nesting requests, which works for one level deep, but does not work two or more levels deep. It also is hostile to logic where skipping some requests is required.
Alamofire.request(...).responseString { formResponse in
Alamofire.request(...).responseString { submitResponse in
Alamofire.request(...).responseString { getDataResponse in
// not only is this messy, it also did not proven to wait until all previous threads were done before working on the nested item.
}
}
}
I have also tried to set up a serial queue with the Alamofire-Synchronous package imported:
let serialQ = DispatchQueue(label: "serialQ")
var info1 = ""
var info2 = ""
var info3 = ""
serialQ.async {
Alamofire.request(...).responseString { formResponse in
....
info1 = "good stuff"
}
}
serialQ.async {
Alamofire.request(...).responseString { submitResponse in
....
if info1 == "x" {...}
info2 = "more good stuff"
}
}
serialQ.async {
Alamofire.request(...).responseString { getDataResponse in
....
if info2 == "x" {...}
info3 = "even more good stuff"
}
}
The above code acts as if that synchronous functionality of the package isn't doing anything to prevent Alamofire from spawning concurrent threads. In other words, it just cranks up all of the requests, one after the other. This might work, I didn't see where my code actually used the code in the package.
I also tried to use the idea presented in this SO answer, but I could not get it working. In fact I worked through all of the answers in that question, and none proved to work for me or some of the "chaining" solutions did not answer the question of how you feed data from one request to the next.
I must me making this harder than it has to be!
Making an http request, waiting for the result, and using it in the next http request, (and the next, and the next) has got to have an elegant swift / Alamofire programming pattern.
A good answer to this question would allow the reader to be able to construct code with the modern/current versions of Swift and Alamofire, that would to chain several http calls where the response from the former request is used in the next request. I've used 3 dependent requests in my examples, but should extend to "n" without getting bogged down.
The Situation:
Via POST operation, users can create a new resource based on given parameters. If there already exists a resource created from these same parameters, the existing resource is returned instead.
Users are able to GET this resource if they know the resource ID (generated on creation, and is effectively random). I would like to provide users a way to check existence only knowing the creation parameters and without creating a new resource.
The Question:
Would it be RESTful to take some kind of "just-checking" property in the POST body to prevent a new resource from being created?
An Example:
POST vehicle
{
colour: 'red',
wheels: 4
}
201: {
vehicleId: '314-159',
colour: 'red',
wheels: 4
}
GET vehicle/314-159
200: {
vehicleId: '314-159',
colour: 'red',
wheels: 4
}
POST vehicle
{
colour: 'red',
wheels: 4,
check: true
}
200: {
vehicleId: '314-159',
colour: 'red',
wheels: 4
}
POST vehicle
{
colour: 'blue',
wheels: 8,
check: true
}
404: Not Found
Edit
Much of the discussion has been around whether the POST operation should be idempotent, which, while valid, does not address my question.
I would like to provide my users with a way to validate the existence of a resource based only on the properties that would be used to create the resource.
The idempotency of the POST method is irrelevant. What suffers from the absence of this check is subsequent GET requests which will contain a number of resources that are never intended to be used, and make it more difficult to find useful information.
A POST request containing a "do-not-create" flag would fill this need, but may not feel RESTful.
How about implementing an idempotent post? In doing so you could avoid the “check” body param.
2 ideas:
Use PUT and natural keys
One option (not sure if this works for you) is to not use some database-id in the url but use something that's a bit more like a natural key.
So instead of POSTing on some collection, you just PUT the item:
PUT /vehicles/colour/blue/wheels/8
PUT can also be used for creation just fine. And you could use a header such as this to prevent overwriting existing values:
If-None-Match: *
Don't put it on the client to do this
What if a POST for creating an item is identical to updating it? Or, what if you call POST on an existing item, it just doesn't actually do anything.
Maybe the client doesn't need to know if it just created a new item, or if the server already had that item.
Just make sure that for those 2 cases the server behaves the same, and you should be good.
Users are able to GET this resource if they know the resource ID (generated on creation, and is effectively random). I would like to provide users a way to check existence only knowing the creation parameters and without creating a new resource.
How would you do it with a web site?
Probably, with a form, that would accept as inputs the same creation parameters. The user is in effect performing a search, which is a semantically safe operation, so the form would likely use the GET method and have the arguments from the form encoded into the query string.
The endpoint, on receiving that request, could redirect it to the appropriate resource (if one already exists) or to another resource to handle the case when it doesn't.
Would it be RESTful to take some kind of "just-checking" property in the POST body to prevent a new resource from being created?
Sure - again, how would you do this on a web site? The form would have an extra checkbox, set to the correct default behavior, but giving the user the option to change it before submitting the form.
Because switching the check box changes the semantics from a safe operation to an unsafe operation, you might want to change the method on the form during submission -- HTML by itself doesn't do that, but you can do it with javascript aka code on demand.
Using POST for safe operations isn't ideal, because generic components can't tell that the operation is safe. This means that they can't know to automatically retry the request if the response is lost, they don't have the correct default cache behaviors, and so on.
For the record, the solution chosen was to add options for a special case on the GET method.
As touched on in this answer, it is not quite in the spirit of the POST method to perform this type of operation, and it muddies the model being presented to the users.
I'm in the middle of upgrading our API from Sails v0.12 -> v1, which was prompted by the use of self-validating machines for controller actions. After finally getting through a ton of headache replacing deprecated code, I've landed in a rough spot...
With v0.12 (rather, with the older "req, res" controller style), one could use custom response handlers across the board. I've taken advantage of this, and have request logging at the end of all our response types (with some additional sugaring of data). This was done to log all requests in the database, so we can get insights into what our production servers are doing (because they are load-balanced, having a central place to view this is a must, and this was an easy route to take).
So now, my problem is moving forward with "Actions2" machine-style actions. How does one use these custom response types in these things? Are we being forced to repeat ourselves in our exists? I can't find any good documentation to help guide this process, nor can I find a consistent way to "hook" into the end of a response using machines as actions. I can't find any documentation on what kind of options machines can give to Sails.
#Nelson yes, I understand that, but at the time, that isn't what I wanted at all. I wanted all of the benefits of Actions2.
EDIT: While the original, crossed-out comment below does still work, the prefered way to use Actions2 and the custom responses folder paradigm, is to do something similar to the following in an Actions2 file:
module.exports = {
friendlyName: 'Human-friendly name of function',
description: 'Long description of function and what it does.',
inputs: {
userCommand: {
type: 'string',
required: true,
description: 'Long, human-readable description of the input'
}
},
exits: {
success: {
responseType: 'chatbotResponse'
}
},
fn: async function(inputs, exits){
// do some crazy stuff with the inputs, which has already been validated.
return exits.success('Woot');
}
}
This ultimately will route through the responses/chatbotResponse.js, which looks something similar to this:
module.exports = async function chatbotResponse(data){
let res = this.res,
req = this.req;
if (!data) {
data = 'Something didn\'t go as planned...';
}
// how to call a Node Machine style helper with named inputs
await sails.helpers.finalizeRequestLog.with({req: req, res: res, body: {plainString: data}});
return res.json(data);
};
ORIGINAL:
As it turns out, in the Actions2 function, you just need to add the env param async function(inputs, exists, env). The env will give you access to the req and res. So, if you have custom responses, that perform special tasks (like request logging), you can just use return await env.res.customResponse('Hurray, you made a successful call!');