I have the following endpoint method:
public class PlayerEndpoint {
private static final String PLAYER_NAME = "player_name";
private static final String PLAYER_UUID = "player_uuid";
#ApiMethod(name = "register", httpMethod = ApiMethod.HttpMethod.POST, path="register")
public Player register(#Named(PLAYER_UUID) String uuid,
#Named(PLAYER_NAME) String playerName) {
log.info(String.format("Registering user uuid: %s name: %s", uuid, playerName));
...
}
}
When I call this from my Android client:
String uuid = "test_uuid";
String name = "test_name";
playerEndpoint.register(uuid, name).execute();
The backend logs:
Registering user uuid: test_name name: test_uuid
What is going on here?
I figured it out. Apparently Endpoints sorts your methods alphabetically.
Method parameters in the generated client library are in alphabetical order, regardless of the original order in the backend method. As a result, you should be careful when editing your methods, especially if there are several parameters of the same type. The compiler will not be able to catch parameter-ordering errors for you.
https://cloud.google.com/developers/articles/google-cloud-endpoints-for-android/
Related
Updating a field contains period (.) is not working as expected.
In docs, nested fields can be updated by providing dot-seperated filed path strings or by providing FieldPath objects.
So if I have a field and it's key is "com.example.android" how I can update this field (from Android)?
In my scenario I've to set the document if it's not exists otherwise update the document. So first set is creating filed contains periods like above and then trying update same field it's creating new field with nested fields because it contains periods.
db.collection(id).document(uid).update(pkg, score)
What you want to do is possible:
FieldPath field = FieldPath.of("com.example.android");
db.collection(collection).document(id).update(field, value);
This is happening because the . (dot) symbol is used as a separator between objects that exist within Cloud Firestore documents. That's why you have this behaviour. To solve this, please avoid using the . symbol inside the key of the object. So in order to solve this, you need to change the way you are setting that key. So please change the following key:
com.example.android
with
com_example_android
And you'll be able to update your property without any issue. This can be done in a very simple way, by encoding the key when you are adding data to the database. So please use the following method to encode the key:
private String encodeKey(String key) {
return key.replace(".", "_");
}
And this method, to decode the key:
private String decodeKey(String key) {
return key.replace("_", ".");
}
Edit:
Acording to your comment, if you have a key that looks like this:
com.social.game_1
This case can be solved in a very simple way, by encoding/decoding the key twice. First econde the _ to #, second encode . to _. When decoding, first decode _ to . and second, decode # to _. Let's take a very simple example:
String s = "com.social.game_1";
String s1 = encodeKeyOne(s);
String s2 = encodeKeyTwo(s1);
System.out.println(s2);
String s3 = decodeKeyOne(s2);
String s4 = decodeKeyTwo(s3);
System.out.println(s4);
Here are the corresponding methods:
private static String encodeKeyOne(String key) {
return key.replace("_", "#");
}
private static String encodeKeyTwo(String key) {
return key.replace(".", "_");
}
private static String decodeKeyOne(String key) {
return key.replace("_", ".");
}
private static String decodeKeyTwo(String key) {
return key.replace("#", "_");
}
The output will be:
com_social_game#1
com.social.game_1 //The exact same String as the initial one
But note, this is only an example, you can encode/decode this key according to the use-case of your app. This a very common practice when it comes to encoding/decoding strings.
Best way to overcome this behavior is to use the set method with a merge: true parameter.
Example:
db.collection(id).document(uid).set(new HashMap<>() {{
put(pkg, score);
}}, SetOptions.merge())
for the js version
firestore schema:
cars: {
toyota.rav4: $25k
}
js code
const price = '$25k'
const model = 'toyota.rav4'
const field = new firebase.firestore.FieldPath('cars', model)
return await firebase
.firestore()
.collection('teams')
.doc(teamId)
.update(field, price)
Key should not contains periods (.), since it's conflicting with nested fields. An ideal solution is don't make keys are dynamic, those can not be determined. Then you have full control over how the keys should be.
When using firebase (or any database that aggregates data basing on ids) I nearly always have to keep track of a key of a given value. For example let's assume I have Location class with latitude and longitude fields. When I download if from firebase, besides its two fields, I also want to keep track of the key (node value generated with push() e.g. -K_esEYXNfMBNmgF3fO4) it was downloaded from so I may later update it, delete it etc. I see only two solutions:
Duplicate the data and add key value as another Location class field. That doesn't work nicely because I have to set the key value only after I executed push().
Create generic wrapper class that will keep key and object:
public class Key<T> {
private final String key;
private final T value;
public Key(String key, T value) {
this.value = value;
this.key = key;
}
public String key() {
return key;
}
public T value() {
return value;
}
}
I am using the second approach but it doesn't look really nice. I have this Key class basically throughout all my codebase and when using RxJava plenty of methods have a return type like this: Observable<Key<Location>> and that just looks ridiculous.
What you call ridiculous actually looks quite normal to me.
Alternatively you can include the key in the POJO and annotate it with #Exclude to exclude it from the serialization.
Follow up on #FrankvanPuffelen great answer, do what you want with the below pushkey
Read and Write Data on Android
private void writeNewPost(String userId, String username, String title, String body) {
// Create new post at /user-posts/$userid/$postid and at
// /posts/$postid simultaneously
String key = mDatabase.child("posts").push().getKey();
Post post = new Post(userId, username, title, body);
Map<String, Object> postValues = post.toMap();
Map<String, Object> childUpdates = new HashMap<>();
childUpdates.put("/posts/" + key, postValues);
childUpdates.put("/user-posts/" + userId + "/" + key, postValues);
mDatabase.updateChildren(childUpdates);
}
I am currently using Sugar ORM and Android Async Http Client for my Android application.
I read through the documentation of Sugar ORM and did exactly what is written there.
My HttpClient is using the singleton pattern and provides methods for calling some APIs.
Now comes the bad part about it. I am not able to save the data persistently into my database which is created by Sugar ORM.
Here is the method, that is calling an API:
public void getAvailableMarkets(final Context context, final MarketAdapter adapter) {
String url = BASE_URL.concat("/markets.json");
client.addHeader("Content-Type", "application/json");
client.addHeader("Accept", "application/json");
client.get(context, url, null, new JsonHttpResponseHandler() {
#Override
public void onSuccess(int statusCode, Header[] headers, JSONArray response) {
Log.i(TAG, "Fetched available markets from server: " + response.toString());
Result<Markets> productResult = new Result<Markets>();
productResult.setResults(new Gson().<ArrayList<Markets>>fromJson(response.toString(),
new TypeToken<ArrayList<Markets>>() {
}.getType()));
ArrayList<Markets> marketsArrayList = productResult.getResults();
// This lines tells me that there are no entries in the database
List<Markets> marketsInDb = Markets.listAll(Markets.class);
if(marketsInDb.size() < marketsArrayList.size() ||
marketsInDb.size() > marketsArrayList.size()) {
Markets.deleteAll(Markets.class);
for(Markets m : marketsArrayList) {
Markets market = new Markets(m.getId(), m.getName(), m.getChainId(), m.getLat(),
m.getLng(), m.getBusinessHourId(), m.getCountry(), m.getZip(), m.getCity(),
m.getStreet(), m.getPhoto(), m.getIcon(), m.getUrl());
market.save();
adapter.add(market);
}
adapter.notifyDataSetChanged();
}
List<Markets> market = Markets.listAll(Markets.class);
// This lines proves that Sugar ORM is not saving the entries
Log.i(TAG, "The market database list has the size of:" + market.size());
}
});
}
This is what Logcat is printing:
D/Sugar: Fetching properties
I/Sugar: Markets saved : 3
I/Sugar: Markets saved : 5
I/RestClient: The market database list has the size of:0
Also I took a look at the Sugar ORM tag here at stackoverflow, but no answers or questions could give me a hint on how to solve that problem.
I am a newbie to the android ecosystem and would love any help of you guys to solve this problem.
Thanks in advance
I just solve it the same problem as you have.
It was a pain in the neck but after few hours I find out what caused this problem.
Using Sugar ORM you must not set id property as it's belongs to SugarRecord class,
otherwise ORM will try to update objects instead of insert them.
As I need to have field with my object id, I used json annotation to assign it to another field.
Last step was configure GSON to exclude fields without Expose annotation.
So my class looks like one below now:
public class MyClass
{
#Expose
#SerializedName("id")
private long myId;
#Expose
private String field1;
#Expose
private String field2;
#Expose
private byte[] field3;
#Expose
private double field4;
public MyClass() { }
// parametrized constructor and more logic
}
Cheers!
I'm getting the weirdest error. Im attempting to store a String to Azure Mobile Service Table and I'm getting this exception. ( Being thrown for a GCM ID I'm generating"
java.lang.NumberFormatException: Invalid int: "C3960965-ECE0-4533-B99D-84DB3881A50F"
public class User
{
private int id;
private String gcmChannel;
public User()
{
}
/**
* Getters and setters
*
*/
public String getRegistrationId() {
return gcmChannel;
}
public final void setRegistrationId(String registrationId) {
gcmChannel = registrationId;
}
There was a change in the tables created in Azure Mobile Services - the tables by default now have ids of type string (instead of integer, as before). If you change your type definition to the following:
public class User
{
private String id;
private String gcmChannel;
// ... rest the same
}
It should work fine. Notice that you'll also need the latest Azure Mobile Services SDK for Android (version 1.1.0) which has support for types with string ids - you can find that SDK at http://www.windowsazure.com/en-us/downloads/?sdk=mobile.
You can find the announcement of this change in the MSDN Forums, or more information about it in this blog post.
Should I paste the actual public key of my app right into the value of this variable?
Or should I encode it and then whatever the encoded string is, I'd make that string into the value of this variable?
Which should it be?
The public key present in your Android Developer Console (which can be found under 'Edit Profile') is already Base64 encoded. Just copy paste the content of the key in your source file. For example, if you have something like this:
Then in your Security.java:
String base64EncodedPublicKey = "MIIBIjANBgkqhkiG9w0BAQ......";
As the Google sample code for In-app billing say, you should obfuscate this public key.
Instead of just storing the entire literal string here embedded in the
program, construct the key at runtime from pieces or
use bit manipulation (for example, XOR with some other string) to hide
the actual key. The key itself is not secret information, but we don't
want to make it easy for an attacker to replace the public key with one
of their own and then fake messages from the server.
I use very simple Java code to generate the Java Class that will give me back the public key. The basic idea is to use recursion to recreate the key using inner static class. It's just food for thought.
It's a "good-enough" approach for my niche market. See this stackexchange security question for more information on obfuscation.
public static void main(String[] args) throws Exception {
String className = genClassName();
PrintWriter writer = new PrintWriter("C:\\" + className + ".java", "iso-8859-1");
printClass(className, writer, "XXXXXX-YOUR-PUBLIC-KEY-GOES-HERE-XXXXXXX", true);
writer.close();
}
private static String genClassName() {
return "Class" + UUID.randomUUID().toString().replaceAll("-", "");
}
private static String printClass(String thisClass, PrintWriter writer, String key, boolean root) {
int split = key.length() / 2;
if (split < 10) {
writer.println("public " + (root ? "" : "static") + " class " + thisClass + " {");
writer.println("public static String get() {");
writer.println("return \"" + key + "\";");
writer.println("}");
writer.println("}");
} else {
String first = key.substring(0, split);
String last = key.substring(split, key.length());
writer.println("public " + (root ? "" : "static") + " class " + thisClass + " {");
String class1 = printClass(genClassName(), writer, first, false);
String class2 = printClass(genClassName(), writer, last, false);
writer.println("public static String get() {");
writer.println("return " + class1 + ".get() + " + class2 + ".get();");
writer.println("}");
writer.println("}");
}
return thisClass;
}
You need the public key in the program's source code so that you can check the signature. Yes, there's nonzero, unavoidable risk that a cracker will find it, replace it with a fake, and feed your program fake purchases.
You cannot completely hide the key from prying eyes, but you can obfuscate. You can break up the Base64 string into several string constants in different spots and concatenate them before use. Better give the chunks inconspicuous names (not like MY_PUBLIC_KEY_PART_4). You can also apply an additional layer of soft encryption to it - something like XOR a value. You can add an integrity check - make sure the key has not been spoofed (say, store the hash of a key elsewhere and check). But this is all still security via obscurity - a determined enough hacker will get through.
Also consider ProGuard, the built-in code obfuscation tool.
If you have a server component as part of your app, then you can move most of the elements of your security, including your public key, to your server. On the server, you can generate the nonce and verify the purchase (I've moved mine to a RESTFul WCF service). If your server component is .NET based, then you'll probably have to generate a modulus and an exponent from your public key so that you can use the RNGCryptoServiceProvider class. There's a Google I/O video which gives an overview to In-App Billing amongst others.