bartonhammond/snowflake

Parse GET from Classes by ID Not Working inside Actions

iSamuelBarney opened this issue · 28 comments

YellowBox.js:69 Possible Unhandled Promise Rejection (id: 0): Cannot read property 'getScrollableNode' of undefined TypeError: Cannot read property 'getScrollableNode' of undefined at AnimatedComponent._detachNativeEvents (http://localhost:8081/index.ios.bundle?platform=ios&dev=true&minify=false&hot=true:54506:24) at AnimatedComponent.componentWillUnmount (http://localhost:8081/index.ios.bundle?platform=ios&dev=true&minify=false&hot=true:54467:6) at http://localhost:8081/index.ios.bundle?platform=ios&dev=true&minify=false&hot=true:23355:13 at measureLifeCyclePerf (http://localhost:8081/index.ios.bundle?platform=ios&dev=true&minify=false&hot=true:23021:8) at ReactCompositeComponentWrapper.unmountComponent (http://localhost:8081/index.ios.bundle?platform=ios&dev=true&minify=false&hot=true:23354:1) at Object.unmountComponent (http://localhost:8081/index.ios.bundle?platform=ios&dev=true&minify=false&hot=true:21620:18) at ReactCompositeComponentWrapper.unmountComponent (http://localhost:8081/index.ios.bundle?platform=ios&dev=true&minify=false&hot=true:23364:17) at Object.unmountComponent (http://localhost:8081/index.ios.bundle?platform=ios&dev=true&minify=false&hot=true:21620:18) at ReactCompositeComponentWrapper.unmountComponent (http://localhost:8081/index.ios.bundle?platform=ios&dev=true&minify=false&hot=true:23364:17) at Object.unmountComponent (http://localhost:8081/index.ios.bundle?platform=ios&dev=true&minify=false&hot=true:21620:18)console.warn @ YellowBox.js:69onUnhandled @ Promise.js:25onUnhandled @ rejection-tracking.js:71(anonymous function) @ JSTimers.js:78callTimer @ JSTimersExecution.js:99callTimers @ JSTimersExecution.js:140__callFunction @ MessageQueue.js:234(anonymous function) @ MessageQueue.js:105guard @ MessageQueue.js:45callFunctionReturnFlushedQueue @ MessageQueue.js:104onmessage @ debuggerWorker.js:44

What am I doing wrong? I'm trying to use GET from /classes//

Where should I be making these calls or how as no examples are included anywhere so I'm just spinning my wheels keep getting this error.

Inside Parse.js this is what my code looks like

async getHost (hostId) { return await this._fetch({ method: 'GET', url: '/classes/hosts/' + hostId }) .then((response) => { return response.json().then(function (res) { if ((response.status === 200 || response.status === 201)) { return res } else { throw (res) } }) }) .catch((error) => { throw (error) }) }

From here: http://parseplatform.github.io/docs/rest/guide/#objects it looks like the URL should be /1/classes/hosts/hostId but you have /classes/hosts/hostId, ie, maybe you need the prefix /1/?

well the endpoint is actually... '/classes/Hosts/'+hostId (correct & tested)

I'm just trying to get data other than the user profile and having a hard time as I'm coming from using sub/unsub/etc so this structure makes no sense to me since my GET call doesn't seem to work either. Is there no guide or example of using Parse in snowflake as far as data?

All of Parse calls are in that Parse.js but there should be examples in
Github just need to search, i guess.

On Thu, Nov 17, 2016 at 3:42 PM, iSamuelBarney notifications@github.com
wrote:

well the endpoint is actually... '/classes/Hosts/'+hostId (correct &
tested)

I'm just trying to get data other than the user profile and having a hard
time as I'm coming from using sub/unsub/etc so this structure makes no
sense to me since my GET call doesn't seem to work either. Is there no
guide or example of using Parse in snowflake as far as data?


You are receiving this because you commented.
Reply to this email directly, view it on GitHub
#168 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/ABORPARJBIlATvJ7z5uiWtZ2-7yuhQooks5q_MpMgaJpZM4K1t8B
.

I've modified everything a bit with but now I only get a promise back not the data... hmm

But no more errors...

looking @ reducers maybe thats what I'm needing...

Maybe @wookiem can help, I really haven't done any development w/ the Parse
open source server but he has, I believe.

On Thu, Nov 17, 2016 at 3:53 PM, iSamuelBarney notifications@github.com
wrote:

I've modified everything a bit with but now I only get a promise back not
the data... hmm


You are receiving this because you commented.
Reply to this email directly, view it on GitHub
#168 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/ABORPMXQjWFiLC_0as59Elg-g0hx_WQ1ks5q_MzXgaJpZM4K1t8B
.

Well I'll keep tinkering & hope @wookiem has time to help. Should figure this out hopefully soon lol

I use the JavaScript API for the main part of my application and it's very straightforward.

could you expand on that a bit?

do you just use the session token to call from inside the component that renders then & import parse/react-native?

Do you have any example code you can share?

@wookiem I get a promise object back with no data when I use the actions/reducers which might be done wrong and probably is. There has to be a simple way to just grab the data once logged in as that's working fine, just can't get anything else.

nvm @wookiem & @bartonhammond so sorry the issue was with setting the REST key & not a JS key...lol lesson learned

Good for you.

It happens to all of us, i was looking for my glasses everywhere this
morning before I realized I was wearing them...

On Nov 17, 2016 5:43 PM, "iSamuelBarney" notifications@github.com wrote:

nvm @wookiem https://github.com/wookiem & @bartonhammond
https://github.com/bartonhammond so sorry the issue was with setting
the REST key & not a JS key...lol lesson learned


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#168 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/ABORPCD8t5xmg1Yv6AXD4ZPGTDJI6yb0ks5q_OaXgaJpZM4K1t8B
.

I am using a more recent version of Parse in package.json:

"dependencies": {
     "parse": "1.9.2",

I initialize Parse within src/snowflake.js:

import Parse from 'parse/react-native';
import CONFIG from '../lib/config';

Parse.initialize( CONFIG.PARSE.appId);
Parse.serverURL = (CONFIG.backend.parseLocal)
? CONFIG.PARSE.local.url
: CONFIG.PARSE.remote.url

I keep track of the currently logged in user and the sessionToken in a separate Redux state called "currentUser". Here's its reducer (note how it ties in with existing snowflake redux states). [Edited based on Barton's comment below]

const {
  GET_PROFILE_SUCCESS,
  SIGNUP_SUCCESS,
  LOGIN_SUCCESS,
  SESSION_TOKEN_SUCCESS,

  LOGOUT_SUCCESS,  
} = require('../../lib/constants').default;


export default function currentUser ( state = {}, action ) {
    let currentUser = null;

  switch (action.type) {
        case SIGNUP_SUCCESS:
        case LOGIN_SUCCESS:
        case GET_PROFILE_SUCCESS:
            currentUser = action.payload.sessionToken;
            return Object.assign({}, {
                    objectId: currentUser.objectId,
                    sessionToken: currentUser.sessionToken,
                    username: currentUser.firstName,
                });     
        case SESSION_TOKEN_SUCCESS:
            currentUser = action.payload.sessionToken;
            return Object.assign({}, {
                    objectId: currentUser.objectId,
                    sessionToken: currentUser.sessionToken,
                    username: currentUser.firstName,
                }); 

        case LOGOUT_SUCCESS:
            return {};

        default: 
            return state;
  }
}

Then, here's sample code for saving data to Parse. A query example, follows the same pattern. Note, "store" refers to the redux store.

import Parse from 'parse/react-native';

export function parseSaveProject( store, dispatch, projectId ) {
    var CurrentProject = Parse.Object.extend("CurrentProject");
    var currentProject = new CurrentProject();

    currentProject.set("projectId", project);

    let sessionToken = store.getState().currentUser.sessionToken;

    currentProject.save(null, {
        sessionToken: sessionToken,
        success: function(currentProject) {
            reduxUpdateParseProjectId(dispatch, currentProject.id);
        },
        error: function(currentProject, error) {
            console.log("Error: Parse did not update current project: ", project.id);           
        }
    });
}

And here's a sample react-native component that would make the call to parseSaveCurrentProject:

class Projects extends Component {

    clickHandler(projectId) {
        var store = this.context.store;
        var dispatch = this.props.dispatch;

        parseSaveProject(store, dispatch, projectId);
    }

  render() {
     return (
      <View onClick={this.clickHandler.bind(this, projectId)}>
        <Text>Click on current Project</Text>
            </View>
        );  
  }
}

Projects.contextTypes = {
    store: React.PropTypes.object
}

const mapStateToProps = (state) => {
    return {
        currentUser: state.currentUser,
    };
};

const mapDispatchToProps = (dispatch) => {
    return { dispatch };
};

export default connect(
        mapStateToProps,
        mapDispatchToProps
)(Projects);

Looks to me that some of those case blocks could be merged as they are
identical?

On Nov 17, 2016 7:07 PM, "wookiem" notifications@github.com wrote:

I am using a more recent version of Parse in package.json:

"dependencies": {
"parse": "1.9.2",

I initialize Parse within src/snowflake.js:

import Parse from 'parse/react-native';
import CONFIG from '../lib/config';

Parse.initialize( CONFIG.PARSE.appId);
Parse.serverURL = (CONFIG.backend.parseLocal)
? CONFIG.PARSE.local.url
: CONFIG.PARSE.remote.url

I keep track of the currently logged in user and the sessionToken in a
separate Redux state called "currentUser". Here's its reducer (note how it
ties in with existing snowflake redux states).

const {
GET_PROFILE_SUCCESS,
SIGNUP_SUCCESS,
LOGIN_SUCCESS,
SESSION_TOKEN_SUCCESS,

LOGOUT_SUCCESS,
} = require('../../lib/constants').default;

export default function currentUser ( state = {}, action ) {
let currentUser = null;

switch (action.type) {
case SIGNUP_SUCCESS:
currentUser = action.payload;
return Object.assign({}, {
objectId: currentUser.objectId,
sessionToken: currentUser.sessionToken,
username: currentUser.firstName,
});
case LOGIN_SUCCESS:
currentUser = action.payload;
return Object.assign({}, {
objectId: currentUser.objectId,
sessionToken: currentUser.sessionToken,
username: currentUser.firstName,
});
case SESSION_TOKEN_SUCCESS:
currentUser = action.payload.sessionToken;
return Object.assign({}, {
objectId: currentUser.objectId,
sessionToken: currentUser.sessionToken,
username: currentUser.firstName,
});
case GET_PROFILE_SUCCESS:
currentUser = action.payload;
return Object.assign({}, {
objectId: currentUser.objectId,
sessionToken: currentUser.sessionToken,
username: currentUser.firstName,
});

    case LOGOUT_SUCCESS:
        return {};

    default:
        return state;

}
}

Then, here's sample code for saving data to Parse. A query example,
follows the same pattern. Note, "store" refers to the redux store.

import Parse from 'parse/react-native';

export function parseSaveProject( store, dispatch, projectId ) {
var CurrentProject = Parse.Object.extend("CurrentProject");
var currentProject = new CurrentProject();

currentProject.set("projectId", project);

let sessionToken = store.getState().currentUser.sessionToken;

currentProject.save(null, {
    sessionToken: sessionToken,
    success: function(currentProject) {
        reduxUpdateParseProjectId(dispatch, currentProject.id);
    },
    error: function(currentProject, error) {
        console.log("Error: Parse did not update current project: ", project.id);
    }
});

}

And here's a sample react-native component that would make the call to
parseSaveCurrentProject:

class Projects extends Component {

clickHandler(projectId) {
    var store = this.context.store;
    var dispatch = this.props.dispatch;

    parseSaveProject(store, dispatch, projectId);
}

render() {
return (
<View onClick={this.clickHandler.bind(this, projectId)}>
Click on current Project

);
}
}

Projects.contextTypes = {
store: React.PropTypes.object
}

const mapStateToProps = (state) => {
return {
currentUser: state.currentUser,
};
};

const mapDispatchToProps = (dispatch) => {
return { dispatch };
};

export default connect(
mapStateToProps,
mapDispatchToProps
)(Projects);


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#168 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/ABORPOFmnPwbxY1AulzgebxvRh6MYpC3ks5q_PpFgaJpZM4K1t8B
.

Yeah, that's a good point!

@wookiem do you have an example of a query? I'm just getting some issues when trying to use the Parse.js file here is my code:

async getHost (hostId) { return await this._fetch({ method: 'GET', url: '/classes/Hosts/' + hostId, }) .then((response) => { return response.json().then(function (res) { if ((response.status === 200 || response.status === 201)) { return res } else { throw (res) } }) }) .catch((error) => { throw (error) }) }

Not sure what I'm doing wrong here

@wookiem getting Error: Permission denied for action get on class Hosts.

The error suggests that Parse class instance hasn't been initialized properly. When you put a breakpoint on the return await this._fetch({ line, do you see that this._sessionToken, this._applicationId and this._ApiBaseUrl are properly set?

If not, then you'll need to add getHost as another method using the pattern established in src/lib/Backend.js and src/lib/BackendFactory.js.

Then you should be able to initiate the promise chain with the following:

BackendFactory().getHost(hostId)
.then((res) {
	// process res
})
.catch((error) {
	// process error
})

Got it working!!!! Thanks all I had to do was add the global.currentUser with the token as an argument just like you suggested. Thanks!!

Thanks for all the help @wookiem one more quick question lol. How would I add relation query constraints like:

where={"phostId":{"__type":"Pointer","className":"Hosts","objectId":"<ID>"}}

it shows in curl curl -X GET ...
-G
--data-urlencode "where={"phostId":{"__type":"Pointer","className":"Hosts","objectId":""}}"
.../classes/Residents

How do i add --data-urlencode into my request?

async getResidents (hostId) { // where={"phostId":{"__type":"Pointer","className":"Hosts","objectId":"<ID>"}} return await this._fetch({ method: 'GET', url: '/classes/Residents/' + hostId, }) .then((response) => { return response.json().then(function (res) { if ((response.status === 200 || response.status === 201)) { return res } else { throw (res) } }) }) .catch((error) => { throw (error) }) }

I'm not sure. After logging in, I switch gears and use the Parse Javascript API for all of my Parse database interactions (i.e. I don't try to use the REST API). I show how to make this switch in my sample code from a couple weeks ago (see above). I find it a lot more intuitive than using the REST API...

Yea seems like I should do the same then.

I got it working!!

FYI if you wanted to see how below.

import qs from 'querystring'
async getResidents (hostId) {
let query = qs.stringify({
where: {"phostId":{"__type":"Pointer","className":"Hosts","objectId":"${hostId}"}}
})
return await this._fetch({
method: 'GET',
url: '/classes/Residents?' + query,
})
.then((response) => {
return response.json().then(function (res) {
if ((response.status === 200 || response.status === 201)) {
return res
} else {
throw (res)
}
})
})
.catch((error) => {
throw (error)
})
}