I've been trying to get the email of the user that submitted a response to a short answer assignment. In all of my other experiences using the Google Classroom API, the "userId" parameter has been the student's email address, but when I call getUserId on the StudentSubmission object, I get a weird string of numbers. How do I get the email associated with the submitted response?
Here's my code for reference:
ListStudentSubmissionsResponse submissionResponse = service.courses().courseWork().studentSubmissions().list(courseId, assignmentId).execute();
List<StudentSubmission> submissions = submissionResponse.getStudentSubmissions();
for (StudentSubmission sub : submissions)
{
System.out.println(sub.getId() + "\t" + sub.getUserId() + "\t" + sub.getState());
}
And this is the response that I am getting:
Cg4I2vWq_8IDEIWck4DDAw 108878473486432178050 CREATED
Does anyone know what is going on here?
There is published documentation on this API... no need to ask here: https://developers.google.com/classroom/reference/rest
More specifically, there are userProfile APIs that differentiate an "id" from an "emailAddress".
https://developers.google.com/classroom/reference/rest/v1/userProfiles
Now that said, there are no guarantees according to the JSON schema, being just a "String" type ... as to what the value might be.
"emailAddress" should be just that but "id" could be whatever the local system uses to identify principals - which is almost definitely NOT going to be the email address. It will instead be some global identifier for whatever principal management system is being used.
Don't not read the docs: retrieve_student_responses
Students are identified by the unique ID or email address of the user, as returned by the Google Admin SDK.
So there's clearly no guarantee it will return the email..
If you read the docs you can find how to properly retrieve the email for a student id: retrieve_a_users_profile
To retrieve the abridged profile, including ID and name, for a user, call userProfiles.get() with the user's ID, email, or "me" for the requesting user.
To retrieve the emailAddress field, you must include the classroom.profile.emails scope.
Related
I recently implemented unique username into my app when registering, all good far here, by the way I also need to set it to when the user is editting it's profile.
I tried to do the same thing, but I'm facing an issue here. The app don't let save the profile, because it's checking if the username's taken and, as we're already using one, it won't let us do it.
Ex.: My username is "bob", I changed my profile pic or my display name, so when I click to save, the app will do a username checking in the background and will not let me save it because the username is already taken, but the problem is that it's already my user.
I've tried to set this, but failed:
if (document.equals(setup_username.getText().toString()) || document.isEmpty()){
updateProfile();
Here's my code:
setup_progressbar.setVisibility(View.VISIBLE);
FirebaseFirestore.getInstance().collection("Users").whereEqualTo("username",setup_username.getText().toString()).get().addOnCompleteListener((task) -> {
if (task.isSuccessful()){
List<DocumentSnapshot> document = task.getResult().getDocuments();
if (document.equals(setup_username.getText().toString()) || document.isEmpty()){
updateProfile();
} else {
setup_progressbar.setVisibility(View.INVISIBLE);
setup_username.setError(getString(R.string.username_taken));
return;
}
} else {
setup_progressbar.setVisibility(View.INVISIBLE);
String error = task.getException().getMessage();
FancyToast.makeText(getApplicationContext(), error, FancyToast.LENGTH_SHORT, FancyToast.ERROR, false).show();
}
});
So how to get around this and only forbid it when I try to change my username to another that is taken? Like: "bob" to "bill", but "bill" is already taken, so it won't allow.
You'll need to have some indication in each Users document to indicate which user has claimed that specific name. Given that you store the username inside the document, ownership would typically be established by using the UID of the user as the ID of the document.
Once you have run your query to find the document for the username, you can then check the UID of the owner of that username against the currently signed in user. If the two UIDs are the same, the current user owns the username and is allowed to update the document.
Compare new username with previous username(store it in a variable while displaying user profile data), if both are same don't update it all else check for its uniqueness.
or if you don't have existing username data create relationship with that document and fetch previous username first.
i am doing android studio project using Firebase,currently i had done with the registration and login,but my function is to transfer amount to another users with balance.
So what i need to do is to find the way to modify/update the balance content(value) after transferring the amount to another user.
but i do some researched they say Firebase only can only read/write with own user account.
assume i have already login to user1,and want to do the transfer to user2.
thanks.
-Users
-AsjdnskkhdiioAndmmnekwas
Name:"User1"
email:"user1#hotmail.com"
uid: "Adjshdkjwwnekwihwoi4kdnw4l2"
age:20
Balance:100
-DdmkenklahaoinskaAnmdmls
Name:"User2"
email:"user2#hotmail.com"
uid: "Cbdnaknekmmalsmdlen1qnio2"
age:21
Balance:100
-------------------------------------------------------------------
**how to become:**
-Users
-AsjdnskkhdiioAndmmnekwas
Name:"User1"
email:"user1#hotmail.com"
uid: "Adjshdkjwwnekwihwoi4kdnw4l2"
age:20
Balance:50
-DdmkenklahaoinskaAnmdmls
Name:"User2"
email:"user2#hotmail.com"
uid: "Cbdnaknekmmalsmdlen1qnio2"
age:21
Balance:150
Remember that you are manager all information of all users (in your database).
Use DatabaseReference setValue() method to update information inside database like this:
private DatabaseReference mDatabase;
mDatabase = FirebaseDatabase.getInstance().getReference();
String senderId = "sample_id_sender";
String receiverId = "sample_id_receiver";
mDatabase.child("users").child(senderId ).child("Balance").setValue(new_balance1);
mDatabase.child("users").child(receiverId).child("Balance").setValue(new_balance2);
How to know id of receiver??? it depends on your situation, example, you want to send 1$ to user whose name is "User2". You must find user2 id in list of users, in you database, I think these guide is good for you:
https://firebase.google.com/docs/database/android/read-and-write
https://firebase.google.com/docs/database/android/lists-of-data
Interesting problem. I think you can use a combination of a modified data model, a transaction and server-side security rules to implement this.
Step-wise:
Since you'll need to know the current balance of both users in order to determine their new balance, you'll need to use a transaction.
You'll need to store the actual transaction in some way, as otherwise the server won't be able to validate the write. The minimum info here is from UID, to UID and amount.
In your security rules you should then modify that the total balance across the two accounts is unmodified across the transaction. For this you need the data from step 2, as you otherwise can't know the to account.
Finally, in your security rules you'll want to validate that the only account whose balance can go down is the one of which the current user is the owner. In rules something like "balance": { ".validate": "data.child('uid').val() == auth.uid || newData.child('Balance').val() > data.child('Balance').val()" }
Scalability may be an issue though, as it's modifying nodes in multiple child nodes which means the transactions run across the entire top-level branch.
Also see:
Is the way the Firebase database quickstart handles counts secure?
I have written a program that reads a webservice, retrieving user data, and then is supposed to push that data to ActiveDirectory, thus updating the user's title, address, phone numbers, etc.
The problem is that when I perform the search using the Unboundid Connection class the requested attributes are not returned. Below is the search code:
SearchResult result = connection.search( properties.getProperty("ldap.search.baseDN"),
SearchScope.SUB, "(cn=" + userId + ")",
"personalTitle", "department", "company", "manager", "telephoneNumber",
"streetAddress", "I", "st", "postalCode", "c", "pager", "mobile",
"fax", "cn");
The above code locates the desired user and the cn attribute is returned as expected, but the other attributes all fail to return. If I connect to AD using JXplorer using the same connection credentials, I'm able to see all the desired attributes exist, but are simply not being returned.
I have tried substituting SearchRequest.ALL_OPERATIONAL_ATTRIBUTES, SearchRequest.ALL_USER_ATTRIBUTES and SearchRequest.REQUEST_ATTRS_DEFAULT rather than listing the fields explicitly, but with no success.
I have also looked at the 'Schema' object returned from 'connection.getSchema()' and can see that personalTitle should exist:
connection.getSchema().getAttributeType("personalTitle")
The above code returns:
1.2.840.113556.1.2.615 NAME 'personalTitle' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE
So maybe this is a user permission issue? Has anyone experienced this and know how to resolve it?
Thanks,
Mike
LDAP search result entries only include attributes that actually have values, so the behavior you are seeing from the UnboundID LDAP SDK is appropriate and correct. Even if you explicitly request a particular attribute, that attribute will only be included in the entry if it has one or more values.
I think that you're confused by JXplorer because it's reading the schema to determine what attributes could possibly be included in the entry based on its object classes and is showing them to you so that you can set values for those attributes in the editor. But that doesn't mean that the entry returned by the server actually includes any information about those attributes.
To verify this, you can use the ldap-debugger tool provided with the LDAP SDK to see the actual LDAP communication that occurs. Just run a command like:
tools/ldap-debugger --hostname {directory-server-address} \
--port {directory-server-port} --listenPort {listen-port}
This will create a very simple LDAP proxy server that decodes all requests and responses that pass through it. To use it, simply point JXplorer at the specified listen-port. You will see that when JXplorer retrieves the entry, the entry returned by the server will only contain attributes that actually have values.
If you want to figure out what all the possible attributes are that you can include in a given entry, then use the LDAPConnection.getSchema method to retrieve the server schema, then Schema.getObjectClass for each of the object classes in the target entry, and finally use the ObjectClassDefinition.getRequiredAttributes and ObjectClassDefinition.getOptionalAttributes methods to see what attribute types must and may be used in entries with that object class.
I am developing a restful webapp.
In this the parameters i take are userid and orderid.
The userid can be null.
The URI is #Path("api/user/userid/order/orderid")
My method is,
void add(#PathParam("userid") String userId, #PathParam("orderid") String orderId);
I want to pass null value for userId in the URI.
I tried api/user//order/1234. But in this case, the userid takes the value of orderId (i.e 1234 and orderId is null. (which is wrong)
I also tried changing the path as #Path("api/user/userid: .*/order/orderid"). Yet the same result as previous.
The other ways for solving this could be, using #QueryParam for userid or creating another method for userid null.
But I would like to know if there is a way to have userId as PathParameter instead of QueryParam and pass the value of userid as null?
Having a URL param as null is not a valid case.
POSTing to the URL api/user/userid/order/orderid is saying "create an order for this user". GETting to that same URL would be saying "retrieve me this specific order for this user". Both are in the context of a user.
For doing order creation or retrieval outside of the context of a user, just remove those two parts of your URL...
api/order/orderid
This would allow you to create and get orders outside of the bounds of a customer. This would also work for in the context of a user. If you POST an order with no userID, nobody cares, but one can be specified. And if you GET an order, it shouldn't care which customer it is for, unless your orderID isn't unique across customers but that is probably not a good idea but that is a separate discussion.
I have a GAE project written in Java and I have some thoughts about the HRD and a problem that I'm not sure how to solve.
Basically I have users in my system. A user consists of a userid, a username, an email and a password. Each time I create a new user, I want to check that there isn't already a user with the same userid (should never happen), username or email.
The userid is the key, so I think that doing a get with this will be consistent. However, when I do a query (and use a filter) to find possible users with the same username or email, I can't be sure that the results are consistent. So if someone has created a user with the same username or email a couple of seconds ago, I might not find it with my query. I understand that ancestors are used to work around this problem, but what if I don't have an ancestor to use for the query? The user does not have a parent.
I'd be happy to hear your thoughts on this, and what is considered to be best practice in situations like these. I'm using Objectify for GAE if that changes anything.
I wouldn't recommend using email or any other natural key for your User entity. Users change their email addresses and you don't want to end up rewriting all the foreign key references in your database whenever someone changes their email.
Here's a short blurb on how I solve this issue:
https://groups.google.com/d/msg/google-appengine/NdUAY0crVjg/3fJX3Gn3cOYJ
Create a separate EmailLookup entity whose #Id is the normalized form of an email address (I just lowercase everything - technically incorrect but saves a lot of pain when users accidentally capitalize Joe#example.com). My EmailLookup looks like this:
#Entity(name="Email")
public class EmailLookup {
/** Use this method to normalize email addresses for lookup */
public static String normalize(String email) {
return email.toLowerCase();
}
#Id String email;
#Index long personId;
public EmailLookup(String email, long personId) {
this.email = normalize(email);
this.personId = personId;
}
}
There is also a (not-normalized) email field in my User entity, which I use when sending outbound emails (preserve case just in case it matters for someone). When someone creates an account with a particular email, I load/create the EmailLookup and the User entities by key in a XG transaction. This guarantees that any individual email address will be unique.
The same strategy applies for any other kind of unique value; facebook id, username, etc.
A way around the HRD's eventual consistency, is to use get instead of query. To be able to do this is you need to generate natural IDs, e.g. generate IDs that consists of data you receive in request: email and username.
Since get in HRD has strong consistency, you will be able to reliably check if user already exists.
For example a readable natural ID would be:
String naturalUserId = userEmail + "-" + userName;
Note: in practice emails are unique. So this is a good natural ID on it's own. No need to add a made-up username to it.
You may also enable cross-group transactions (see https://developers.google.com/appengine/docs/java/datastore/overview#Cross_Group_Transactions) and then in one transaction look for the user and create a new one, if that helps.
Recommend avoiding an indexed field and query unless you have other uses for it. Here is what I have done before (Python) using key_name (since entity ids need to be ints). Easy to use either the key_name or id for other entities that need to link to user:
username = self.request.get('username')
usernameLower = username.lower()
rec = user.get_by_key_name(usernameLower)
if rec is None:
U = user(
key_name = usernameLower,
username = username,
etc...)
U.put()
else:
self.response.out.write(yourMessageHere)