I am currently making a call to my server to make a change in the Datastore and listening for another change to it. I do not receive any new changes from the DB despite the database being changed correctly on the backend.
I recently upgraded amplify from 1.4.2 to 1.6.8 and this is when the issue started to show up.
Amplify.DataStore.observe(Profile::class.java,
{ observationCancelable ->
//ERROR
},
{ changedItem ->
when (changedItem.item().state) {
//DO Business logic
}
},
{ exception ->
//ERROR
},
{
}
)
When I query the datastore I am getting outdated results that are not the same as the data on DynamoDB. Is there any way to figure out why the local datastore is not being updated? Could it be an issue with the configuration with the server? Or is there some setup step that I have missed?
Edit: Added some extra details below
So a few more details I wanted to add to this that seem way more relevant as well as updates based on my research. I am switching between endpoints(dev to testing environments). It would seem that the dev environment is working fine with the application, using the datastore correctly and doing the proper AppSync. But in the new testing environment, when the app starts, it gets the latest version of the datastore but it fails to do any AppSync despite being able to make changes to the backend.
Here is an error I am getting when the app attempts to subscribe to the Profile object in the back end
amplify:aws-datastore: Unauthorized failure for ON_CREATE Profile
amplify:aws-datastore: Releasing latch due to an error: Subscription error for Profile: [GraphQLResponse.Error{message='Not Authorized to access onCreateProfile on type Subscription', locations='null', path='null', extensions='{errorType=Unauthorized}'}]
Is there some config file that I should be looking at to compare with to make sure that the endpoints match or some access key that the app needs?
Related
I have to improve existing Ionic app using firebase as auth method for users login.
For my purpose i cannot touch existing list of previously registered users in production firebase app, I have to create new list locally using firebase emulator.
After adding and setup local auth & DB emulator extensions, I have added this into app.module.ts:
if (window.location.hostname === 'localhost') {
firebase.auth().useEmulator('http://localhost:9099');
}
After continuing with adding new UI and logical features, using a web browser to test my work, I switch to the Android platform. After running the app and executing
firebase.auth().signInWithEmailAndPassword(email, password);
I got the following error:
{
"code": "auth/network-request-failed",
"message": "A network error (such as timeout, interrupted connection, or unreachable host) has occurred."
}
It worked fine in the browser, but not on real device.
Whereas on Android device
window.location.hostname === 'localhost
this will be executed
firebase.auth().useEmulator('http://localhost:9099');
so Firebase searching auth service on device on port 9099, that not exists, and produce such error.
To fix this just run in terminal
adb reverse tcp:9099 tcp:9099
Brief information: I am working on a quiz application for Android. The database is on Firebase and the users login via anonymously. When the user opened the application, it will be automatically signed-in.
My question is about firebase. I could not build the intelligence for firebase requests.
When the application is opened;
1) signInAnonymously (which firebase function) should be called first.
2) Then i check that the signed user has a saved point or not on firebase database.
3) If the user does not have point, it is generated.
4) Then i send a request to get the point of user.
In all steps, i send a request to firebase via async firebase methods. The sequence is important because the output of any step can be an input for the next step.
I handle this via callback. But i do not know that it is the best way.
screenshots of callbacks for these steps
Can you give me advice for these? If i do not use callbacks, problems are occured because of asynchronous firebase methods. The reason of that i open this issue is undetermined problems. I can learn and build any other algorithm to make it better. Thank you.
It looks like you are using nested callbacks and I am not a Java programmer, but you may want to take it easy on yourself and not go that route.
If my signing in anonymously you mean a One-Time-Password authentication flow such as just providing a phone number, that would definitely be a good approach.
You can use Google Cloud Functions, but the functions would have to be written in Nodejs, Python or Go.
Either way take a look at this flow below:
User requests OTP
Acknowledge the request
Generate code, save the code on backend (GCF)
Text user the code
User sends you the correct code
Compare codes on the server
Send user some kind of token or as you say a "point" to identify them.
I do believe Java does have support for JSON Web Tokens.
So after your setup GCF project, you are going to get some folder and files like so:
.firebaserc: a hidden file that helps you quickly switch between projects with firebase use.
firebase.json: describes properties for your project.
functions/: this folder contains all the code for your functions.
functions/package.json: an NPM package file describing your Cloud Functions.
functions/index.js: the main source for your Cloud Functions code.
functions/node_modules/: the folder where all your NPM dependencies are installed.
You want to import the needed modules and initialize the app:
const admin = require("firebase-admin");
const functions = require("firebase-functions");
const serviceAccount = require("./service_account.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://my-project.firebaseio.com"
});
That service_account.json is something you need to create, its not a library.
It will have a bunch of private and public keys that you get from your Firebase console. Ensure you also place that file inside your .gitignore files as well.
So I am skipping some crucial details here that you will have to figure out so as to get to your main question.
First, you need the idea of a user, so you need to create a user inside GCF so that in itself is going to be a function and as you mentioned Firebase is asynchronous so it would look something like this:
const admin = require("firebase-admin");
module.exports = function(req, res) {
// Verify the user provided a phone
if (!req.body.phone) {
return res.status(422).send({ error: "Bad Input" });
}
// Format the phone number to remove dashes and parens
const phone = String(req.body.phone).replace(/[^\d]/g, "");
// Create a new user account using that phone number
admin
.auth()
.createUser({ uid: phone })
.then(user => res.send(user))
.catch(err => res.status(422).send({ error: err }));
// Respond to user request saying account was made
};
So the code above I grabbed from a previous project of mine, except the whole thing was in JavaScript. For you this part will be in JavaScript or Nodejs specifically as well since again, Nodejs, Go or Python are the only languages supported by GCF.
So the comments are self-explanatory but I feel compelled to explain that the first thing I had to resolve is how to pass in information to this function in a request.
To do that I had to reference the req.body object as you see above. req.body contains all the different data that was passed to this function when the user called it. I was not sure if you knew that. So before you go and copy paste what I have above, do a res.send(req.body);. So nothing else inside that module.exports = function(req, res) {} except res.send(req.body);, so you can get a good sense of how this all works.
For every function you create you need to run a firebase deploy name-of-project.
After you feel you have a handle on this and its all working successfully, you can create your Android Studio project and add the database dependency like so:
compile 'com.google.firebase:firebase-database:10.2.1'
And then you will probably want to create your User model, maybe like this:
public class User {
public String phone;
public User() {
// Default constructor required for calls to DataSnapshot.getValue(User.class)
}
public User(String phone) {
this.phone = phone;
}
}
And so on, anyway I hope that kind of gives you a good enough idea that it gets you going. Best of luck. I know I failed to take time out to explain that the regex in my code is to sanitize the phone number and probably some other stuff. So again, don't just copy paste what I offered, study it.
Tried to use the azure mobile app for my cordova project but the problem is when i am offline and i enter the data, the data is stored locally and upon going online the data is synced to the database, but
while we enter a record offline, close the app then reopen the app and again add the record the latest record is overwritten to the local db and the earlier records are lost while syncing to the azure database.
syncContext = client.getSyncContext();
// Define an overly simplified push handler that discards
// local changes whenever there is an error or conflict.
// Note that a real world push handler will have to take action according
// to the nature of conflict.
syncContext.pushHandler = {
onConflict: function (pushError) {
return pushError.cancelAndDiscard();
},
onError: function (pushError) {
return pushError.cancelAndDiscard();
}
};
return syncContext.initialize(store);
});
Anyways got the answer to the problem had to replace
pushError.cancelAndDiscard();
call to
pushError.appendAndDiscard();
and worked excellent as required for me
Using the Mobile Backend Starter (MBS) Android classes (those distributed as a sample project when creating a new project in Google Dev Console and demoed at Google I/O 2013) I'm able to insert new entities to the cloud datastore via calls to CloudBackendMessaging.insertAll or .updateAll. The latter will create entities if none exist so seems functionally identical to insert for new records.
The insertion/creation works fine. However when I attempt to update existing entries in the datastore, I received permissions errors e.g. (from the backend log)
Method: mobilebackend.endpointV1.updateAll
Error Code: 401
Reason: required
Message: Insuffient permission for updating a CloudEntity: XXXXXX by: USER: YYYYYYY
which results in a matching access error in the logcat client side.
In all cases I am using Secured access authenticating with a valid Google account (my own).
The entities being inserted are thus showing as "owned" by my user ID with "updated by" and "created by" showing my Google account's email address.
However when the update of the existing record is made, using exactly the same CloudBackendMessenger object and thus same credentials etc. the backend is telling me I can't update due to permissions issues. But surely if I just made the entity with the same credentials this can't be correct? Looking at the documentation it appears that I should be able to edit entities owned by the same user ID in all cases (regardless of the KindName and whether it is prepended [public], [private] or nothing).
Can anyone who has received permissions errors on UPDATES via Mobile Backend Starter for Datascore please shed any light? I have been banging my head over this for most of today.
I've faced the similar error "Insuffient permission for updating a CloudEntity" when using cloudBackendAsync.update(cloudEntity). I resolved it by making sure the cloudEntity has it's createdAt field set. createdAt is autogenerated and I think I am not supposed to touch it. But it worked for me. In my case I am first obtaining list of cloud entities. This is when I get createdAt field of cloud entities. Then when I am updating I setting the createdAt field from previously obtained entities.
Edit: Had to do similar thing for owner field also.
Similar to one of the comments above, I successfully got around this by getting the original CloudEntity before doing the insert/update/delete function.
CloudQuery cq = new CloudQuery("datastoretype");
cq.setLimit(1);
cq.setFilter(Filter.eq("_id",id));
cloudEntity.setId(id);
mProcessingFragment.getCloudBackend().get(cloudEntity, handler);
Thereafter it was trivial to do the following:
mProcessingFragment.getCloudBackend().update(cloudEntity, handler);
The docs definitely ought to be more clear on this, whether it is a strict requirement or bug.
The answers posted so far work around the problem if you don't mind all users being able to access the entity you are trying to update. However, a better solution that retains the access permissions is detailed by google here - https://cloud.google.com/cloud/samples/mbs/authentication
If you want to pass the user’s Google Account info to the backend on
each call, use the CloudBackend#setCredential() method (also available
on the subclasses, CloudBackendAsync and CloudBackendMessaging) to set
a GoogleAccountCredential object before calling any Mobile Backend
methods.
GoogleAccountCredential credential = GoogleAccountCredential.usingAudience(this, "<Web Client ID>");
credential.setSelectedAccountName("<Google Account Name>");
cloudBackend.setCredential(credential);
Setting credientials enables the client to operate when the backend is
in “Secured by Client ID” mode and also sets createdBy/updatedBy/owner
properties of CloudEntity automatically.
I am wondering what is required to setup a server so that you can store data on it, and then have an application send requests to it to store and receive data. More specifically, I am working on an Android application where a user will generate data and then that should be stored on a server so other users can access it. But I do not know how setting up a server to be capable of this works. I have worked on Android applications in the past that sends requests (put, post, get, etc) to a server, but that back end was already set up for me. Any info or resources about setting this up would be great.
There are many, many different ways to accomplish this.
Since you're already working with a Google technology, Android - you could start by creating a Google App Engine project. Following the tutorials you can get started setting up a simple back end solution that will store data for you and you can make requests to it for that data.
Another advantage to this for you is that you don't have to learn how to install software on a server and all the dependencies that arise from that, etc. Simply set up a new account and push-button deploy through Eclipse or command line.
And since you've used Java in Android, you can use JAva for Google App Engine (GAE) too!
Getting started: http://code.google.com/appengine/docs/java/gettingstarted/introduction.html
You can try ready to use BAAS/PAAS services to store your data, e.g. QuickBlox for Android http://quickblox.com/developers/Android, where you can manipulate with your data with few strings
QBLocation location = new QBLocation();
location.setLatitude(35.0);
location.setLongitude(53.0);
location.setStatus("I'm at this place");
String someImportantString = "Dr. Henry Walton Indiana Jones";
QBLocations.createLocation(location, new QBCallbackImpl() {
#Override
public void onComplete(Result result, Object context) {
// retrieve context object inside callback
String passedContextObject = (String) context;
System.out.println(passedContextObject);
// do stuff with result
}
}, someImportantString);
All logic of data exchange with server is encapsulated in framework.