Getting albums from Facebook using ionic-native/facebook - facebook-graph-api

I am using the ionic-native/facebook plugin to get the albums for users who have authorised my app.
After a user has clicked the "log in with facebook" link in the app they are taken to Facebook, they approve the app and then get sent back. At this point I make an API call to get their albums:
this.fb.api('/me/?fields=id,name,albums{name,picture,cover_photo{images}}', this.fbPermisions)
.then((res: any) => {
console.log(res);
})
.catch(this.handleError);
This returns an error in the console: Error processing action - "There was an error making the graph call"
If however I try either of the following calls then they successfully execute.
this.fb.api('/me', this.fbPermisions)
.then((res: any) => {
console.log(res);
})
.catch(this.handleError);
Or
this.fb.api('/me/?fields=id,name,albums', this.fbPermisions)
.then((res: any) => {
console.log(res);
})
.catch(this.handleError);
All three calls work absolutely fine if I use the Graph API Explorer.
So, does anybody know why /me/?fields=id,name,albums{name,picture,cover_photo{images}} fails within ionic-native/facebook but works via the Graph API Explorer?

It seems like urlencoding the request gives me what I need.
So instead of:
this.fb.api('/me/?fields=id,name,albums{name,picture,cover_photo{images}}', this.fbPermisions)
I used:
this.fb.api('/me/?fields=id%2Cname%2Calbums%7Bname%2Cpicture%2Ccover_photo%7Bimages%7D%7D', this.fbPermisions)
And voila, it worked .. !

Related

AWS Amplify post request fails with "status code 403 at node_modules/axios"

I configured and initialized AWS Amplify for my ReactNative/Expo app and added a REST Api. Im new to AWS in general, but im assuming that once I add the API, my project is populated with amplify/backend folders and files and is ready for consumption.
So i tried to create a simple post request to create an item in my DynamoDB table with
import { Amplify, API } from "aws-amplify";
import awsconfig from "./src/aws-exports";
Amplify.configure(awsconfig);
const enterData = async () => {
API.post("API", "/", {
body: {
dateID: "testing",
},
headers: {
Authorization: `Bearer ${(await Auth.currentSession())
.getIdToken()
.getJwtToken()}`
}
})
.then((result) => {
// console.log(JSON.parse(result));
})
.catch((err) => {
console.log(err);
});
};
const signIn = async () => {
Auth.signIn('test#test.com', 'testpassword')
.then((data) => {
console.log(data)
enterData() //enterData is attempted after signin is confirmed.
})
.catch((err) => {
console.log(err)
})
}
signIn()
I did not touch anything else in my project folder besides including the above in my App.tsx because im unsure if i need to and where. I got a 403 error code and it "points" to the axios package but im not sure if issue is related to aws integration.
I configured the REST Api with restricted access where Authenticated users are allowed to CRUD, and guests are allowed to Read. How could I even check if I am considered an "Authorized User" .
Yes, AWS Amplify API category uses Axios under the hood so axios is related to your problem.
Probably you get 403 because you didn't authorized, for Rest API's you need to set authorization headers,
I don't know how is your config but you can take help from this page. Please review the "Define Authorization Rules" section under the API(REST) section.
https://docs.amplify.aws/lib/restapi/authz/q/platform/js/#customizing-http-request-headers
To check authorization methods, you can use "Auth" class like that also you can see auth class usage in the above link.
import { Amplify, API, Auth } from "aws-amplify";
https://aws-amplify.github.io/amplify-js/api/classes/authclass.html

I am using htmx javascript API how do I get the response from server

My code block looks like this, basically when a modal is triggered I send a GET request and retrieve the response, it does send the request alright, but I am unable to see (or to put it better) to get the response from the server via .then() from htmx. I am using the example from there documentation.
htmx.ajax('GET',
'/user-related-comment/',
{ swap: 'none', values: { userId: userId } }
).then(data => {
console.log(data)
})
data is undefined when logged to browser console.
For anyone that needs to do this, an option will be to listen to 'htmx:afterOnLoad' event example.
htmx.ajax('GET', '/example', '#myDiv').then(() => {
document.body.addEventListener('htmx:afterOnLoad', event=>{
console.log(event)
// access response at event.detail.xhr.response
// convert to JavaScript object by JSON.parse(event.detail.xhr.response)
})
});
For some reason on first click, does nothing.

Amplify PubSub not sending/receiving - no errors

I have been struggling with this implementation and figured I'd ask for some community perspective at this point.
I've implemented PubSub with a Lambda successfully and when tested in the cloud I am seeing messages in the IoT test environment. I believe, therefore, that my endpoint is functional.
When trying to implement the Amplify service via the docs (https://docs.amplify.aws/lib/pubsub/getting-started/q/platform/js/) I have been running into all sorts of issues. Worked through the "socket:undefined" issue by reinstalling lock file and node-modules. Now am not getting any errors but it simply is not connecting.
My code is below. Currently when I try to publish I'm getting a response of []. If I try to specify the provider I get this error - "Error: Could not find provider named AWSIoTProvider".
Note: I have been following various SOs - this one most recently:
Amplify PubSub javascript subscribe and publish using cognito authorization: how to?
import Amplify from 'aws-amplify';
import { AWSIoTProvider } from '#aws-amplify/pubsub/lib/Providers';
import PubSub from '#aws-amplify/pubsub';
Amplify.addPluggable(new AWSIoTProvider({
aws_pubsub_region: 'as-southeast-2',
aws_pubsub_endpoint: 'wss://{MY_IOT_ID}-ats.iot.ap-southeast-2.amazonaws.com/mqtt',
}));
Amplify.configure(config);
PubSub.configure();
PubSub.subscribe('myTopic1').subscribe({
next: data => console.log('Message received', data),
error: error => console.error(error),
complete: () => console.log('Done'),
});
Then I have a function that I'm calling for publish that returns the [] if I don't specify the provider and the error above if I specify it (shown below)
Unspecified:
await PubSub.publish('1234-abcd-9876/workitem', { msg: 'Hello to all subscribers!' })
.then(response => console.log('Publish response:', response))
.catch(err => console.log('Publish Pub Err:', err));
Specified:
await PubSub.publish('1234-abcd-9876/workitem', { msg: 'Hello to all subscribers!' }, { provider: 'AWSIoTProvider' })
.then(response => console.log('Publish response:', response))
.catch(err => console.log('Publish Pub Err:', err));
Does anyone have any thoughts as to what I might be doing wrong here or might try next?
Thanks!
Since you are saying the specific error you got is - "Error: Could not find provider named AWSIoTProvider".
so, change the import path for AWSIoTProvider to #aws-amplify/pubsub instead of using #aws-amplify/pubsub/lib/Providers.
import { AWSIoTProvider } from '#aws-amplify/pubsub;

AWS X-Ray (with SailsJS) not logging things in the correct trace?

I am trying to use AWS X-Ray in my SailsJS application. I noticed missing subsegments - I added custom traces via AWSXRay.captureAsyncFunc but noticed they are missing. After some closer inspection, I think they actually ended up in a different trace. Lets say I call login API then another API later. I notice my login API trace is weird
Notice theres calls quite abit after the request should have ended.
Those requests should actually be in another segment:
I'd think they should appear after find device subsegment. Why are segments scrambled like that?
My setup: in http.js,
const AWSXRay = require('aws-xray-sdk');
const xrayEnabled = process.env.AWS_XRAY === 'yes'
module.exports.http = {
middleware: {
order: [
'startRequestTimer',
'cookieParser',
'session',
'myRequestLogger',
'bodyParser',
'handleBodyParserError',
'compress',
'methodOverride',
'poweredBy',
'awsXrayStart',
'router',
'awsXrayEnd',
'www',
'favicon',
'404',
'500',
],
awsXrayStart: xrayEnabled ? AWSXRay.express.openSegment(`cast-${process.env.NODE_ENV || 'noenv'}`) : (req, res, next) => next(),
awsXrayEnd: xrayEnabled ? AWSXRay.express.closeSegment() : (req, res, next) => next(),
Then I wrapped my promises like:
instrumentPromise(promise, name, metadata = {}) {
if (this.isXrayEnabled()) {
return new Promise((resolve, reject) => {
AWSXRay.captureAsyncFunc(name, (subsegment) => {
if (!subsegment) console.warn(`[XRAY] Failed to instrument ${name}`)
Object.keys(metadata).forEach(k => {
if (subsegment) subsegment.addMetadata(k, metadata[k])
})
console.time(`[XRAY TIME] ${name}`)
promise
.then((data) => {
if (subsegment) subsegment.close()
console.timeEnd(`[XRAY TIME] ${name}`)
resolve(data)
})
.catch(err => {
if (subsegment) subsegment.close()
console.timeEnd(`[XRAY TIME] ${name}`)
reject(err)
})
})
})
}
return promise
}
Is there any information I am missing here? What am I doing wrong?
I tried manual mode and its alot more reliable but I have to manually pass segment around. Whats wrong with automatic mode? I am kind of guessing it does not work well with async nature nodejs? Like the SDK is not able to differenciate between the various async requests? And may close or track segments in the wrong places? That said ... its supposed to work with express, why isit not working as expected ...
Another thing is how will a shared mysql connection pool be tracked correctly by X-Ray? Different segments will be using the same mysql pool. I assume this will not work work well at all?
The issue you encounter seems to be related to how CLS handle context binding with Promise. There is a opt-in promise patch introduced in this PR https://github.com/aws/aws-xray-sdk-node/pull/11. It has full discussion around the repros and fixes. That should resolve the issue with subsegments being attached to the wrong trace.
The SDK does support capturing pool.query. You can see examples here https://www.npmjs.com/package/aws-xray-sdk-mysql.

Preserve cookies / localStorage session across tests in Cypress

I want to save/persist/preserve a cookie or localStorage token that is set by a cy.request(), so that I don't have to use a custom command to login on every test. This should work for tokens like jwt (json web tokens) that are stored in the client's localStorage.
To update this thread, there is already a better solution available for preserving cookies (by #bkucera); but now there is a workaround available now to save and restore local storage between the tests (in case needed). I recently faced this issue; and found this solution working.
This solution is by using helper commands and consuming them inside the tests,
Inside - cypress/support/<some_command>.js
let LOCAL_STORAGE_MEMORY = {};
Cypress.Commands.add("saveLocalStorage", () => {
Object.keys(localStorage).forEach(key => {
LOCAL_STORAGE_MEMORY[key] = localStorage[key];
});
});
Cypress.Commands.add("restoreLocalStorage", () => {
Object.keys(LOCAL_STORAGE_MEMORY).forEach(key => {
localStorage.setItem(key, LOCAL_STORAGE_MEMORY[key]);
});
});
Then in test,
beforeEach(() => {
cy.restoreLocalStorage();
});
afterEach(() => {
cy.saveLocalStorage();
});
Reference: https://github.com/cypress-io/cypress/issues/461#issuecomment-392070888
From the Cypress docs
For persisting cookies: By default, Cypress automatically clears all cookies before each test to prevent state from building up.
You can configure specific cookies to be preserved across tests using the Cypress.Cookies api:
// now any cookie with the name 'session_id' will
// not be cleared before each test runs
Cypress.Cookies.defaults({
preserve: "session_id"
})
NOTE: Before Cypress v5.0 the configuration key is "whitelist", not "preserve".
For persisting localStorage: It's not built in ATM, but you can achieve it manually right now because the method thats clear local storage is publicly exposed as Cypress.LocalStorage.clear.
You can backup this method and override it based on the keys sent in.
const clear = Cypress.LocalStorage.clear
Cypress.LocalStorage.clear = function (keys, ls, rs) {
// do something with the keys here
if (keys) {
return clear.apply(this, arguments)
}
}
You can add your own login command to Cypress, and use the cypress-localstorage-commands package to persist localStorage between tests.
In support/commands:
import "cypress-localstorage-commands";
Cypress.Commands.add('loginAs', (UserEmail, UserPwd) => {
cy.request({
method: 'POST',
url: "/loginWithToken",
body: {
user: {
email: UserEmail,
password: UserPwd,
}
}
})
.its('body')
.then((body) => {
cy.setLocalStorage("accessToken", body.accessToken);
cy.setLocalStorage("refreshToken", body.refreshToken);
});
});
Inside your tests:
describe("when user FOO is logged in", ()=> {
before(() => {
cy.loginAs("foo#foo.com", "fooPassword");
cy.saveLocalStorage();
});
beforeEach(() => {
cy.visit("/your-private-page");
cy.restoreLocalStorage();
});
it('should exist accessToken in localStorage', () => {
cy.getLocalStorage("accessToken").should("exist");
});
it('should exist refreshToken in localStorage', () => {
cy.getLocalStorage("refreshToken").should("exist");
});
});
Here is the solution that worked for me:
Cypress.LocalStorage.clear = function (keys, ls, rs) {
return;
before(() => {
LocalStorage.clear();
Login();
})
Control of cookie clearing is supported by Cypress: https://docs.cypress.io/api/cypress-api/cookies.html
I'm not sure about local storage, but for cookies, I ended up doing the following to store all cookies between tests once.
beforeEach(function () {
cy.getCookies().then(cookies => {
const namesOfCookies = cookies.map(c => c.name)
Cypress.Cookies.preserveOnce(...namesOfCookies)
})
})
According to the documentation, Cypress.Cookies.defaults will maintain the changes for every test run after that. In my opinion, this is not ideal as this increases test suite coupling.
I added a more robust response in this Cypress issue: https://github.com/cypress-io/cypress/issues/959#issuecomment-828077512
I know this is an old question but wanted to share my solution either way in case someone needs it.
For keeping a google token cookie, there is a library called
cypress-social-login. It seems to have other OAuth providers as a milestone.
It's recommended by the cypress team and can be found on the cypress plugin page.
https://github.com/lirantal/cypress-social-logins
This Cypress library makes it possible to perform third-party logins
(think oauth) for services such as GitHub, Google or Facebook.
It does so by delegating the login process to a puppeteer flow that
performs the login and returns the cookies for the application under
test so they can be set by the calling Cypress flow for the duration
of the test.
I can see suggestions to use whitelist. But it does not seem to work during cypress run.
Tried below methods in before() and beforeEach() respectively:
Cypress.Cookies.defaults({
whitelist: "token"
})
and
Cypress.Cookies.preserveOnce('token');
But none seemed to work. But either method working fine while cypress open i.e. GUI mode. Any ideas where I am coming short?
2023 Updated on Cypress v12 or more:
Since Cypress Version 12 you can use the new cy.session()
it cache and restore cookies, localStorage, and sessionStorage (i.e. session data) in order to recreate a consistent browser context between tests.
Here's how to use it
// Caching session when logging in via page visit
cy.session(name, () => {
cy.visit('/login')
cy.get('[data-test=name]').type(name)
cy.get('[data-test=password]').type('s3cr3t')
cy.get('form').contains('Log In').click()
cy.url().should('contain', '/login-successful')
})