Sveltekit Todos Example... Where is the data being stored? - javascript

I'm learning how SvelteKit works. There is a pretty nice "hello world PLUS" place to start with the "create-svelte" example. In that example there is a "todos" page. Source files here. It's a simple page where you can add an delete things to do tasks. And I'm trying to figure out how the individual tasks are managed.
I can see where an todos array is created in Javascript on the client; I can verify that via console.logs in index.svelte file. I can see via Chrome Inspect (dev tools, network tab) where the creation of a task generates a todos.json network Post request http://localhost:3000/todos.json The get/post request simply returns the original content back to the client.
What I'm not seeing is where the data is being stored on the browser. I was fully expecting to see something in local storage, session storage, cookies or even indexeddb.
What I'm not seeing is where that content is being stored. If I close the browser tab and reopen, the data is there. If I open a different browser on the same local computer, no task data exists. If I "empty cache and hard reload" the task data remains.
After doing some testing, I can see one cookie.
Name: userid Value: d38b9b44-1a8b-414e-9a85-1c2b2c0700f4 Domain: localhost Path: / Expires/MaxAge: Session Size: 42 HttpOnly: ✓ Priority: Medium
If I modify or delete this cookie in any way then the stored task data disappears.
So where is the todos task data being temporarily stored? What am I not seeing?

I read the header for this file _api.js multiple times. I tested "https://api.svelte.dev" in the browser and got back null responses.. I just assumed that this was a dead server.
It turns out that the folks at svelte do offer a fully functional api server, both receiving, deleting and storing todo task data. See my test notes within api function.
A browser request to https://api.svelte.dev/todos/d38b9b44-1a8b-414e-9a85-1c2b2c0700f4 absolutely returns my task data, and now I can see how this stuff works. Info offered here in case anybody else is wondering what's going on here.
Here is the complete _api.js file
/*
This module is used by the /todos.json and /todos/[uid].json
endpoints to make calls to api.svelte.dev, which stores todos
for each user. The leading underscore indicates that this is
a private module, _not_ an endpoint — visiting /todos/_api
will net you a 404 response.
(The data on the todo app will expire periodically; no
guarantees are made. Don't use it to organise your life.)
*/
const base = 'https://api.svelte.dev';
export async function api(request, resource, data) {
console.log("resource: ", resource);
// returns--> resource: todos/d38b9b44-1a8b-414e-9a85-1c2b2c0700f4
//https://api.svelte.dev/todos/d38b9b44-1a8b-414e-9a85-1c2b2c0700f4 <--- test this !!
// user must have a cookie set
if (!request.locals.userid) {
return { status: 401 };
}
const res = await fetch(`${base}/${resource}`, {
method: request.method,
headers: {
'content-type': 'application/json'
},
body: data && JSON.stringify(data)
});
// if the request came from a <form> submission, the browser's default
// behaviour is to show the URL corresponding to the form's "action"
// attribute. in those cases, we want to redirect them back to the
// /todos page, rather than showing the response
if (res.ok && request.method !== 'GET' && request.headers.accept !== 'application/json') {
return {
status: 303,
headers: {
location: '/todos'
}
};
}
return {
status: res.status,
body: await res.json()
};
}

Related

UserState showing null in onMembersAdded function

I have logic in my onMembersAdded function to load the user state and see if userData.accountNumber attribute exists. If it does not, a run an auth dialog to get the user's account number. If the attribute does exist, the welcome message should be displayed without a prompt.
When I test on local, this works fine. But when I test on Azure, I always end up in the !userData.accountNumber block. Through checking the console log, I can see that in the onMembersAdded function is showing {} for the userData object. But in auth dialog, even if I skip the prompt (which we allow the user to do), the accountNumber attribute is there in userState (if it had been entered previously).
The only thing I can figure is that somehow using BlobStorage for state, as I do on Azure, is somehow exhibiting different behavior than MemoryStorage which I am using for local testing. I thought it might be a timing issue, but I am awaiting the get user state call, and besides if I do enter an account number in the auth dialog, the console log immediately following the prompt shows the updated account number, no problem.
EDIT: From the comments below, it's apparent that the issue is the different way channels handle onMembersAdded. It seems in emulator both bot and user are added at the same time, but on webchat/directline, user isn't added until the first message is sent. So that is the issue I need a solution to.
Here is the code in the constructor defining the state variables and onMembersAdded function:
// Snippet from the constructor. UserState is passed in from index.js
// Create the property accessors
this.userDialogStateAccessor = userState.createProperty(USER_DIALOG_STATE_PROPERTY);
this.dialogState = conversationState.createProperty(DIALOG_STATE_PROPERTY);
// Create local objects
this.conversationState = conversationState;
this.userState = userState;
this.onMembersAdded(async (context, next) => {
const membersAdded = context.activity.membersAdded;
for (let member of membersAdded) {
if (member.id === context.activity.recipient.id) {
this.appInsightsClient.trackEvent({name:'userAdded'});
// Get user state. If we don't have the account number, run an authentication dialog
// For initial release this is a simple prompt
const userData = await this.userDialogStateAccessor.get(context, {});
console.log('Members added flow');
console.log(userData);
if (!userData.accountNumber) {
console.log('In !userData.accountNumber block');
const dc = await this.dialogs.createContext(context);
await dc.beginDialog(AUTH_DIALOG);
await this.conversationState.saveChanges(context);
await this.userState.saveChanges(context);
} else {
console.log('In userData.accountNumber block');
var welcomeCard = CardHelper.GetHeroCard('',welcomeMessage,menuOptions);
await context.sendActivity(welcomeCard);
this.appInsightsClient.trackEvent({name:'conversationStart', properties:{accountNumber:userData.accountNumber}});
}
}
}
// By calling next() you ensure that the next BotHandler is run.
await next();
});
If you want your bot to receive a conversation update from Web Chat with the correct user ID before the user sends a message manually, you have two options:
Instead of connecting to Direct Line with a secret, connect with a token (recommended). Note that this will only work if you provide a user property in the body of your Generate Token request.
Have Web Chat send an initial activity to the bot automatically so the user doesn't have to. This would be in response to DIRECT_LINE/CONNECT_FULFILLED, and it could be an invisible event activity so to the user it still looks like the first activity in the conversation came from the bot.
If you go with option 1, your bot will receive one conversation update with both the bot and the user in membersAdded, and the from ID of the activity will be the user ID. This is ideal because it means you will be able to acess user state.
If you go with option 2, your bot will receive two conversation update activities. The first is the one you're receiving now, and the second is the one with the user ID that you need. The funny thing about that first conversation update is that the from ID is the conversation ID rather than the bot ID. I presume this was an attempt on Web Chat's part to get the bot to mistake it for the user being added, since Bot Framework bots typically recognize that conversation update by checking if the from ID is different from the member being added. Unfortunately this can result in two welcome messages being sent because it's harder to tell which conversation update to respond to.
Conversation updates have been historically unreliable in Web Chat, as evidenced by a series of GitHub issues. Since you may end up having to write channel-aware bot code anyway, you might consider having the bot respond to a backchannel event instead of a conversation update when it detects that the channel is Web Chat. This is similar to option 2 but you'd have your bot actually respond to that event rather than the conversation update that got sent because of the event.
Per Kyle's answer, I was able to resolve the issue. However, the documentation on initiating a chat session via tokens wasn't entirely clear, so I wanted to provide some guidance for others trying to solve this same issue.
First, you need to create an endpoint in your bot to generate the token. The reason I initiated the session from SECRET initially was because I didn't see a point to creating a token when the SECRET was exposed anyway to generate it. What wasn't made clear in the documentation was that you should create a separate endpoint so that the SECRET isn't in the browser code. You can/should further obfuscate the SECRET using environmental variables or key vault. Here is the code for the endpoint I set up (I'm passing in userId from browser, which you'll see in a minute).
server.post('/directline/token', async (req, res) => {
try {
var body = {User:{Id:req.body.userId}};
const response = await request({
url: 'https://directline.botframework.com/v3/directline/tokens/generate',
method: 'POST',
headers: { Authorization: `Bearer ${process.env.DIRECTLINE_SECRET}`},
json: body,
rejectUnauthorized: false
});
const token = response.token;
res.setHeader('Content-Type', 'text/plain');
res.writeHead(200);
res.write(token);
res.end();
} catch(err) {
console.log(err);
res.setHeader('Content-Type', 'text/plain');
res.writeHead(500);
res.write('Call to retrieve token from Direct Line failed');
res.end();
}
})
You could return JSON here, but I chose to return token only as text. Now to call the function, you'll need to hit this endpoint from the script wherever you are deploying the bot (this is assuming you are using botframework-webchat CDN). Here is the code I used for that.
const response = await fetch('https://YOURAPPSERVICE.azurewebsites.net/directline/token', {
method: 'POST',
headers: {'Content-Type':'application/json'},
body: JSON.stringify({userId:userID})
});
const token = await response.text();
Body of request must be stringified JSON. Fetch returns the response as a stream, so you need to convert it using .text() or .json() depending on how you are sending the response from your bot endpoint (I used .text()). You need to await both the fetch AND the response.text(). My whole script to deploy the webchat is within an async function. Just a note, if you need this to work in IE11 as I do, async/await won't work. I dealt with this by running the entire code through Babel once I was done and it seems to work fine.

Using JWT saved in cookies in vue.js to get a user object from my spring API for persisted log-in

I'm trying to mock up some persisted log-in for my first web application so the site is still functional after a refresh. When I print the token (which is saved in cookies) in the console, it prints normally. And when I use postman with the token in the header, I get the correct JSON response. However, when using it in the mounted method, I get a 401. So I believe it is an issue with the way I'm am implementing my headers in my fetch. Thanks in advance, as I am extremely new to coding.
mounted: function() {
console.log(this.$cookies.get('token'));
let t = JSON.parse(JSON.stringify(this.$cookies.get('token')));
let h = new Headers();
h.append('Authentication', `Bearer ${t}`);
fetch('http://localhost:8080/api/owner/persist', {
method: 'GET',
headers: h
})
.then((response) => {
return response.json();
})
.then((data) => {
this.jwtUser = data;
})
Java Controller below: if I have the PreAuthorize Tag, I get a 401 error, and if I take it away I get a null pointer exception. I think its just something wrong with the formatting of my header. Which I have been messing around with a lot.
#PreAuthorize("isAuthenticated()")
#RequestMapping(path = "api/owner/persist", method = RequestMethod.GET)
public Owner persistedLogin(Principal principal) {
Owner o = new Owner();
o = ownerDAO.getOwnerInfoByName(principal.getName());
return o;
}
The standard way to transport access tokens, and especially JWTs, is the header called Authorization.
In your code example you are using Authentication which is from a description point of view correct as JWTs are in the first step authenticating a request and only at the second step source for authorization. But the standard header is like it is and was named Authorization. Your formatting of the header-value (Bearer <token>) looks correct to me.
Double check the correct name of your header that needs to carry the token, and verify you are using the correct one which is working as you stated in your test with Postman.
Best,
cobz

serviceworker is it possible to add headers to url request

I have a website which I don't want to make people create accounts. It is a news feed with each news article categorized. I want to allow people to tag the categories they are interested in so that next time they go to the site it only shows news for the categories that are tagged.
I'm saving the tags in an indexedDB which I understand is available in a service worker.
Hence in my service worker I want to "intercept" requests to www.my-url.com, check the indexDB for what categories this person is interested in, and add some headers like 'x-my-customer-header': 'technology,physics,sports' so that my server can respond with a dynamic html of those categories only.
However I'm struggling to get the service worker to properly cache my root response. In my serviceworker.js, I console log every event.request for the onFetch handler. There are no requests that are related to my root url. I'm testing right now on my localhost, but I only see fetch requests to css & js files.
Here is my onFetch:
function onFetch(event) {
console.log('onFetch',event.request.url);
event.request.headers["X-my-custom-header"] = "technology,sports";
event.respondWith(
// try to return untouched request from network first
fetch(event.request).catch(function() {
// if it fails, try to return request from the cache
caches.match(event.request).then(function(response) {
if (response) {
return response;
}
// if not found in cache, return default offline content for navigate requests
if (event.request.mode === 'navigate' ||
(event.request.method === 'GET' && event.request.headers.get('accept').includes('text/html'))) {
return caches.match('/offline.html');
}
})
})
);
}
I'm using rails so there is no index.html that exists to be cached, when a user hits my url, the page is dynamically served from my news#controller.
I'm actually using the gem serviceworker-rails
What am I doing wrong? How can I have my service worker save a root file and intercept the request to add headers? Is this even possible?
Credit here goes to Jeff Posnick for his answer on constructing a new Request. You'll need to respond with a fetch that creates a new Request to which you can add headers:
self.addEventListener('fetch', event => {
event.respondWith(customHeaderRequestFetch(event))
})
function customHeaderRequestFetch(event) {
// decide for yourself which values you provide to mode and credentials
// Copy existing headers
const headers = new Headers(event.request.headers);
// Set a new header
headers.set('x-my-custom-header', 'The Most Amazing Header Ever');
// Delete a header
headers.delete('x-request');
const newRequest = new Request(event.request, {
mode: 'cors',
credentials: 'omit',
headers: headers
})
return fetch(newRequest)
}

How to run one request from another using Pre-request Script in Postman

I'm trying to send an authenticated request with one click in postman.
So, I have request named "Oauth" and I'm using Tests to store the token in a local variable.
var jsonData = JSON.parse(responseBody);
postman.setEnvironmentVariable("token", jsonData.access_token);
What I'm trying to do now is that run the Oauth request automatically (from a pre-request script) for any other requests which needs a bearer token.
Is there a way to get an access token and send an authenticated request with one postman button click?
As mentioned by KBusc and inspired from those examples you can achieve your goal by setting a pre-request script like the following:
pm.sendRequest({
url: pm.environment.get("token_url"),
method: 'GET',
header: {
'Authorization': 'Basic xxxxxxxxxx==',
}
}, function (err, res) {
pm.environment.set("access_token", res.json().token);
});
Then you just reference {{access_token}} as any other environment variable.
NOTE: There now is a way to do this in a pre-request script, see the other answers. I'll keep this answer for posterity but just so everyone knows :)
I don't think there's a way to do this in the pre-request script just yet, but you can get it down to just a few clicks if you use a variable and the Tests tab. There are fuller instructions on the Postman blog, but the gist of it is:
Set up your authentication request like normal.
In the Tests section of that request, store the result of that request in a variable, possibly something like the following:
var data = JSON.parse(responseBody);
postman.setEnvironmentVariable("token", data.token);
Run the authentication request -- you should now see that token is set for that environment (click on the eye-shaped icon in the top right).
Set up your data request to use {{token}} wherever you had previously been pasting in the bearer token.
Run your data request -- it should now be properly authenticated.
To refresh the token, all you should need to do is re-run the authentication request.
You can't send another request from Pre-request Script section, but in fact, it's possible to chain request and run one after another.
You collect your request into collection and run it with Collection Runner.
To view request results you can follow other answer.
The same question was on my mind, which is basically "how can I run another request that already exists from another request's test or pre-request script tabs without building that request with pm.sendRequest(reqConfObj)?", then I found the postman.setNextRequest('requestName') method from this Postman discussion which is gonna lead you to this postman documentation page about building request workflows.
But the thing is, postman.setNextRequest() method will not run if you are not running a folder or a collection, so simply hitting the 'Send' button of the request that has your script won't work.
I also would like to draw your attention towards some things:
The prepended word, it's 'postman' instead of 'pm'.
postman.setNextRequest() will always run last, even though you have written it to the top of your script. Your other code in the script will be ran and then postman.setNextRequest will initialize.
If you would like to stop the request flow, you could simply postman.setNextRequest(null).
I would encourage everyone that uses Postman to check out the links that was mentioned, I believe it's a great feature that everybody should give it a try! :)
All these workarounds with recreating requests. Postman does not support what you want to do. In order to get what you want, you have to use Insomnia, it allows you to map body values from other request responses and if those responses are not executed ever it automatically runs them or behaves based on chosen policy.
But if you want to stick with Postman, then you'll have to save full previous request params to global variables, then retrieve all configuration of previous requests from that variable as a JSON string, parse that JSON into an object and assign it to pm.sendRequest as the first argument.
You can add a pre-request script to the collection which will execute prior to each Postman request. For example, I use the following to return an access token from Apigee
const echoPostRequest = {
url: client_credentials_url,
method: 'POST',
header: 'Authorization: Basic *Basic Authentication string*'
};
var getToken = true;
if (!pm.environment.get('token')) {
console.log('Token missing')
} else {
console.log('Token all good');
}
if (getToken === true) {
pm.sendRequest(echoPostRequest, function(err, res) {
console.log(err ? err : res.json());
if (err === null) {
console.log('Saving the token');
console.log(res);
var responseJson = res.json();
console.log(responseJson.access_token);
pm.environment.set('token', responseJson.access_token)
}
});
}
First, add pre-request script:
pm.sendRequest({
url: 'http://YOUR_SITE',
method: 'POST',
body: {
mode: 'urlencoded',
urlencoded: [
{ key: "login", value: "YOUR_LOGIN" },
{ key: "password", value: "YOUR_PASSWORD" }
]
}
}, function (err, res) {
if (err === null) {
pm.globals.set("token", res.json()["access_token"]);
}
});
Second, set custom variable(after you can use it value):
Third, you can use variable by {{VARIABLENAME}}, for example:
If you are setting token in your auth token you can copy its request configuration to env once (in test script section) and automatically call it from other request and use token from env.
It originally mentuioned here: https://community.postman.com/t/use-existing-postman-request-in-pre-post-script/23477/5
Copy current request config to env in auth test script:
let r=pm.response.json();
pm.environment.set("access_token", r.access_token);
pm.environment.set("refresh_token", r.refresh_token);
pm.environment.set("auth_req", pm.request);
And then call it on other endpoints:
pm.sendRequest(pm.environment.get("auth_req"))
I have tried multiple solutions, the below solution is related to when you are parsing the response for request 1 and passing any variable into the second request parameter. ( In this Example variable is Lastname. )
Note:- data and user are JSON objects.``
postman.clearGlobalVariable("variable_key");
postman.clearEnvironmentVariable("variable_key");
tests["Body matches string"] = responseBody.has("enter the match string ");
var jsonData = JSON.parse(responseBody);
var result = jsonData.data;
var lastName = result.user.lastName;
tests["Body matches lastName "] = responseBody.has(lastName);
tests["print matches lastName " + lastName ] = lastName;
You can use a function sendRequest of postman.
Some examples here.
https://gist.github.com/sid405/b57985b0649d3407a7aa9de1bd327990

Save data in localstorage of browers in ember simple auth

I have used ember-simple-auth for fb login.I am able fetch the fb's access_token and sending the token to my server for exchange of server token.This works fine and and I am able to make my transition to user feed page.But the problem I am facing is for the first time user.I have to show them some onboarding screen before taking them to their feed screen.And from next time I can skip the onboarding process for them.
torii.js
export default Torii.extend({
torii: service('torii'),
authenticate() {
return new RSVP.Promise((resolve, reject) => {
this._super(...arguments).then((data) => {
console.log(data.accessToken);
raw({
url: 'http://example.co.in/api/socialsignup/',
type: 'POST',
dataType: 'json',
data: { 'access_token':data.accessToken,'provider':'facebook'}
}).then((response) => {
console.log(response.token);
resolve({
// jscs:disable requireCamelCaseOrUpperCaseIdentifiers
access_token: response.token,
// jscs:enable requireCamelCaseOrUpperCaseIdentifiers
provider: data.provider
});
}, reject);
}, reject);
});
}
});
The response for I am getting from my server is something like this.
For First Time User.
{access_token:'abcd...',signup_done:false}
When Signup_done is false I have to show user the onboarding screen and the make a POST request to server "http://example.co.in/api/post/signupdone"
For normal user
{access_token:'abcd...',singup_done:true}
This time I have to just move the user to thier the feed screen skipping the onboarding screen.
I want to save the singup_done from server to my cookie or brower local storage ( not sure ).So that I can use this value in my handlebars,controller,model,component.I am also open to other suggestion to achieve this with simple method.Please use code to example.Thanks in advance.
You can save data in the session's data by just setting it:
this.get('session').set('data.signupDone', true);
That way signupDone will be persisted (also when the session is invalidated). Of course if the user logs in in another browser that data won't be present anymore so you should probably store the property in a resource on the server side instead.

Categories

Resources