Google Drive Android Api Completion Event for Folder Creation - google-drive-android-api

The completion events in Google Drive Android Api (GDAA) seem to be invoked only by contents change (file create, file contents update). Since I need to retrieve a Resource Id of a folder (seen here for a file with contents), referring to this method:
DriveFolder.createFolder(mGAC, meta).setResultCallback(...);
I need a completion event for a folder creation, but I can't find a solution.
Any hints? Thank you.

No, takers for this question, so I assume there is no straightforward solution. Meanwhile, I slammed together a hack until this gets fixed (if ever).
1/ Create a folder, you get the 'preliminary' DriveId that YIELDS NO ResourceId (nothing's commited).
2/ Use this DriveId as a parent of a dummy file with a dummy content and a completion event request attached. The folder creation is now apparently forced by it's child.
3/ In the completion event (as seen here), get the dummy file's DriveId and retrieve it's parent ID:
com.google.android.gms.common.api.GoogleApiClient GAC;
//...
DriveId parentID(DriveId dId) {
MetadataBuffer mdb = null;
DriveApi.MetadataBufferResult mbRslt = dId.asDriveResource().listParents(GAC).await();
if (mbRslt.getStatus().isSuccess()) try {
mdb = mbRslt.getMetadataBuffer();
if (mdb.getCount() > 0)
return mdb.get(0).getDriveId();
} catch (Exception e) { e.printStackTrace();}
finally {
if (mdb != null) mdb.close();
}
return null;
}
('await()' flavor works here since 'DriveEventService' is off-UI)
Now you get folder's 'real' DriveId that can produce a valid ResourceId:
String resourceId = driveId.getResourceId()
4/ zap the dummy file that has served it's lifetime mission
Is there a better solution? Please let me know.

Related

HMS Drive kit Query and query string

I'm a beginner developer just starting with HMS. I'm trying to write a code that finds the "profile.json" file, which was previously uploaded to the cloud into the folder "DebtSorter". My code (Kotlin) for it currently looks like:
var fileList: MutableList<File?> = mutableListOf()
thread {
try {
val drive = buildDrive()
val request = drive.files().list()
var cursor: String?
fileList = ArrayList()
do {
var result = request.setQueryParam("fileName contains 'profile'")
.setOrderBy("fileName")
.setPageSize(10)
.setFields("*")
.execute()
for (file in result.files) {
fileList.add(file)
}
cursor = result.nextCursor
request.cursor = cursor
} while (!StringUtils.isNullOrEmpty(cursor))
} catch (e: java.lang.Exception) {
Toast.makeText(applicationContext, "executeFilesList exception: $e", Toast.LENGTH_LONG)
.show()
}
}
return fileList
However, the returned fileList is always empty, even when I upload the file to the root of the cloud. I think I'm messing something up with the query string, because I can upload files just fine. Can someone tell me the syntax and keywords used in the query string, and possibly help me with the code and how to search in a given folder in HMS Drive kit?
Update:
You can also refer to the demo: https://github.com/HMS-Core/hms-drive-serverdemo In the demo, the thread executes the method of obtaining the file list directly.
Your query parameters is incorrect. You can't define the format of your query string. Please refer to this sample query statement.
(assuming that the ID of folder A is f192358798744098816):
To query all files in folder A, run the following statement: 'f192358798744098816' in parentFolder and mimeType != 'application/vnd.huawei-apps.folder'
To query all folders in the recycle bin, run the following statement: mimeType='application/vnd.huawei-apps.folder' and recycled=true
The query criteria 'fileId' in parentFolder and recycled=true cannot be used together, that is, a user cannot query files or folders in a specified parent folder in the recycle bin.
For more details, see the official guide.
In the end, the problem was with creating the thread. Since the thread is asynchronous and I created the fileList variable before the thread, modified it in the thread and returned it after the thread, the actual modification happened after the method already returned with the empty fileList. I took out the lines with thread and put the method call into a thread. This solved it.

recursive lambda function never seems to run

I'm not familiar enough with node.js or lambda to see an obvious solution to a dilemma I have. I'm writing some utilities on lambda to manipulate images in an S3 bucket and make them accessible via the GatewayAPI to rest calls.
BACKGROUND DETAILS:
One of the utilities I have retrieves the headObject information such as the mtime, size and metadata. The images themselves will likely be coming in from various means and I won't always have control over adding metadata to them when they arrive/are-created. But I don't really need it until it's necessary to view details about the image from a web interface. And when I do that, I use a thumbnail instead so I created a lambda create-event triggered script (and also have a fall back variation of it via the gatewayAPI) that will create a thumbnail (either when the image is first uploaded to S3 or whenever I make the gateway CreateThumbbnail call) at which time it adds metadata to the thumbnail for the image with things like the original image mimetype, pixel width and height.
What I would like to be able to do, is to create a 'GetObjectInfo' that firsts pulls the headObject data, then checks to see if the bucket specified is or is not the bucket with the associated thumbnail files. (e.g. if it is or is not a thumbnail object) If it is 'not' a thumbnail, I want to then go retrieve -- or at least attempt to retrieve -- the headObject for the associated thumbnail file and attach the thumbnail file's metadata (if the thumbnail exists) onto the data from the original head request before returning the information.
The problem is, that when I set up an async callback scheme, the first headObject request completes, the second never seems to get out of the starting gate.
The method in my class is:
getHeadObject(bucket,object,callback) {
console.log(bucket, "CLASS-head#1")
this.s3.headObject({"Bucket":bucket,"Key":object}, function(err,data){
console.log(bucket, "CLASS-head#2")
callback(err,data)
})
}
getObjectInfo(bucket,object,callback) {
let scope = this
console.log(bucket,"CLASS-object#1")
this.getHeadObject(bucket,object,function(err,data) {
console.log(bucket,"CLASS-object#2")
if(err)
callback(err,data)
else
callback(null,data)
})
}
The lambda code that calls it recursively is:
var cInst = new myClass()
cInst.getObjectInfo(srcBucket,filePath,function(err,data) {
if(data.status == 1) { // if parent request success
// if parent is not thumbnail
if(srcBucket != THUMB_BUCKET) { // see if a thumbnail exists
let thumbPath = myClass.getThumbPath(srcBucket,userId,directory,targetObject)
console.log('---- thumbPath', thumbPath)
cInst.getObjectInfo(THUMB_BUCKET,thumbPath, function(err,thumbData) {
console.log("thumbData #1",thumbData)
if(thumbData.status == 1) { // thumbnail exists
console.log("thumbData")
}
})
}
context.succeed(myClass.createResponse(1, data, api))
} else {
context.fail(myClass.createResponse(data.status, data, api))
}
})
First call on the parent is see
{bucket} "CLASS-object#1"
{bucket} "CLASS-head#1"
{bucket} "CLASS-head#2"
{bucket} "CLASS-object#2"
on the second I only see:
image-thumbnails "CLASS-object#1"
image-thumbnails "CLASS-head#1"
(getThumbPath is just a static utility function that builds the thumbnail path based on the parameters related to the original file. It is already tested as working and produces something like {original-bucket-name}/{userid}/{subdirectory}/{file-basename_150x150.jpg} for any given image - I confirmed that in this instance, the thumbnail exists and matches the path returned by getThumbPath and the acl appears to have permission to read the bucket and the object)
UPDATE: More weirdness
I tried setting the permissions to publicly readable on the thumbnail and it worked. So I started messing with the acl. For the time being since I am still testing, I just gave the role for the scripts full S3 permissions.
But I noticed now that it's working and not working intermittently. One time it completes, the next time it doesn't. WTF is going on here?
I would bet that this is the most common problem that people see when using Node.js with Lambda.
When a Node.js Lambda reaches the end of the main thread, it ends all other threads. When it reaches the end of the handler, it stops all concurrent promises or async calls that are running.
To make sure that the lambda does not prematurely terminate those threads, wait until those promises are complete by using await.
In your case, the following will work: wrap any async calls in a promise and then await them.
await new Promise(async (resolve, reject) => {
cInst.getObjectInfo(srcBucket,filePath,function(err,data) {
if(data.status == 1) {
if(srcBucket != THUMB_BUCKET) {
...
...
await new Promise((resolve2, reject2) => {
cInst.getObjectInfo(THUMB_BUCKET,thumbPath, function(err,thumbData) {
...
...
resolve2();
})
})
}
context.succeed(myClass.createResponse(1, data, api))
resolve();
} else {
context.fail(myClass.createResponse(data.status, data, api))
reject();
}
})
})

A confusing situation on my way to understand DDD

Thank you in advance for your help and attentation!
My project is dedicated only for learning purposes and I'm totally confused with DDD and have the following situation:
There is the ubiquitous language of my domain where I have users and documents. It says the following:
- A user can create a document. One of the main purpose of my project is to provide users an ability to create different documents. I mean that the documents cannot exist without the users. So,I think that the process of a document creation belongs to my domain.
- A user can send a document for approval. It's one more thing that belongs to the domain. An approval process is one of the most important part of the project. It has its steps that other users must confirm.
- A user can approve a step of approval process.
- A user can reject a step of approval process.
That's enough to understand and answer my question:
Is it normal that a User can contain such methods as: CreateDocument(params), SendDocumentForApproval(docId), ApproveApprovalStepOfDocument(stepId)?
I'm comfused with it because It looks in code a bit strange.
For example for the document creatation process we have something like that:
public async Task<bool> CreateDocumentCommandHandler(CreateDocumentCommand command)
{
//We have our injected repositories
User user = await _userRepository.UserOfId(command.UserId);
Document document = User.CreateDocoment(command.*[Params for the document]);
_documentRepostiory.Add(document);
// It raises event before it makes a commit to the database
// It gets event from an entity. The entity keeps it as readonly collection.
// Here it raises DocumentCreatedEvent. This event contains logic which concerns
// creation some additional entities for the document and log actions.
await _documentRepository.UnitOfWork.SaveEntitiesAsync();
}
The approval process:
//The first try out to model this process:
public async Task<bool> SendDocumentForApprovalCommandHandler(SendDocumentForApprovalCommand command)
{
//We have our injected repositories
User user = await _userRepository.UserOfId(command.UserId);
//Here I have some problems.
//Is it okay that the method returns the document?
//The method that is placed inside the User has this logic:
//public Document SendDocumentForApproval(int docId)
//{
// Document document = this.GetDocument(docId);
//
// //Inside this method ChangedStatusToApproving is created
// document.SetStatusToApproving();
// return document;
//}
Document document = User.SendDocumentForApproval(command.DocId);
_documentRepostiory.Upadate(document);
// It raises event before it makes a commit to the database
// It gets event from an entity. The entity keeps it as readonly collection.
// Here it raises ChangedStatusToApproving. This event contains logic which concerns
// creation some additional entities for the document and log actions.
await _documentRepository.UnitOfWork.SaveEntitiesAsync();
}
//Is it okay to do something like the command handler above?
//The second one:
public async Task<bool> SendDocumentForApprovalCommandHandler(SendDocumentForApprovalCommand command)
{
//We have our injected repositories
User user = await _userRepository.UserOfId(command.UserId);
//The same one as we have in the previous method.
//But here I don't want to put the logic about the changing status of the doucnent inside it.
Document document = User.SendDocumentForApproval(command.DocId);
//I see that it breaks the method above (SendDocumentForApproval)
//Now It doesn't mean anything for our domain, does it?
//It is only getter like User.GetDocument or we can even do it
//by using repository - documentRepository.DocumentOfId(docId)
document.SetStatusToApproving();
_documentRepostiory.Upadate(document);
await _documentRepository.UnitOfWork.SaveEntitiesAsync();
}
// So, I think the first one is better, isn't it? It follows the ubiquitous language.
//And here is the final question: Why can't I do it like this:
public async Task<bool> SendDocumentForApprovalCommandHandler(SendDocumentForApprovalCommand command)
{
//Here we don't want to use the userRepository. We don't need at all
//Here as a consequence we also don't need a user entity
//Everything what we need is:
Document document = _documentRepository.DocOfId(command.DocId);
document.ForApproval();
_documentRepostiory.Upadate(document);
await _documentRepository.UnitOfWork.SaveEntitiesAsync();
}
//I think that the last approach breaks the ubiquitous language and we're about to having an anemic model.
//But here we have only two queries to the database because we don't need a user.
//Which of the approaches is better? Why? How can I do it more correctly if I want to apply DDD?
I want to explain my thoughts in more details.
Let's have a look at the user. They manage documents. A Document cannot exist without the user. Does it mean that the User is an aggregate root through we need to create, update, delete its aggregates.
And the document is also an aggregate root due to it contains an apporval process. The ApprovalProcess cannot exist without the document.
Does it mean that I need to do something like this:
public async Task<bool> SendDocumentForApprovalCommandHandler(SendDocumentForApprovalCommand command)
{
Document document = _documentRepository.DocumentOfId(command.DocId);
document.SendForApproval();
_documentRepository.SaveChangesAsync();//Raise a domain event - SentDocumentForApprovalEvent
}
// Here we have a handler for the event SentDocumentForApprovalEvent
public async Task SentDocumentForApprovalEventHandler(SentDocumentForApprovalEvent sentDocumentForApprovalEvent)
{
//Now I want to create an approval process for the document
//Can I do the next thing:
ApprovalProcess process = new ApprovalProcess(sentDocumentForApprovalEvent.DocId);
_approvalProcessRepository.Add(process);
_approvalProcessRepository.SaveEntitiesAsync();//Raise a domain event - InitiatedApprovalProcessEvent
//Or Should I create the approval process through Document?
//Looks terrible due to we need to call the repostiory amd
ApprovalProcess process = Document.InitiateApprovalProcess(sentDocumentForApprovalEvent.DocID);//Static method
_approvalProcessRepository.Add(process);
_approvalProcessRepository.SaveEntitiesAsync();
//log
}
// Here we have a handler for the event InitiatedApprovalProcessEvent
public async Task InitiatedApprovalProcesEventHandler(SentDocumentForApprovalEvent sentDocumentForApprovalEvent)
{
//The same question as we have with handler above.
//Should I create steps trough the approval process or just with the help of constructor of the step?
//log
}
Thank you so much and sorry for my terrible English!
Best regards
Is it normal that a User can contain such methods as: CreateDocument(params), SendDocumentForApproval(docId), ApproveApprovalStepOfDocument(stepId)?
In most domain models, the method belongs with the entity that manages the state that is going to change.
Document document = User.SendDocumentForApproval(command.DocId);
_documentRepository.Update(document);
The fact that your sample is updating the document repository here is a big hint that it is the document that is changing, and therefore we would normally expect to see SendDocumentForApproval as a method on the document.
document.SendDocumentForApproval(command.UserId)
_documentRepository.Update(document);
(Yes, the code doesn't read like written or spoken English.)
When creating a new document... creation patterns are weird. Udi Dahan suggests that there should always be some entity in your domain model that is responsible for creating the other entities, but I'm not convinced that the result is actually easier to work with in the long term.
How can we model the approval business process
General answer: business processes are protocols, which is to say that you can normally model them as a state machine. Here's the state we are in right now, here is some new information from the outside world, compute the consequences.
(Often, the data model for a process will just look like a history of events; the domain model's job is to then take the new information and compute the right events to store in the history. You don't have to do it that way, but there are interesting possibilities available when you can).
You are headed in a right direction, User and Document both are aggregates as they are created in separate transactions. When it comes to who references whom, IDDD principle of scalability says that aggregates should refer aggregates only via their IDs.
I think sticking to the ubiquitious, language your code should look something like this
class User {
private UserId id;
private String name;
User(String name) {
this.id = new UserId();
this.name = name;
}
Document createDocument(String name) {
Document document = new Document(name);
document.createdBy(this);
return document;
}
Document approve(Document document) {
document.approved();
return document;
}
}
class Document {
private DocumentId id;
private String name;
private UserId userId;
private Boolean isApproved;
Document(String name) {
this.id = new DocumentId();
this.name = name;
}
void createdBy(UserId userId) {
this.userId = userId;
}
void approved() {
this.isApproved = true;
}
}
// User creation
User user = new User("Someone");
userRepository.save(user);
//Document creation
User user = userRepository.find(new UserId("some-id"))
Document document = user.createDocument("important-document")
documentRepository.save(document)
// Approval
User user = userRepository.find(new UserId("some-id"))
Document document = documentRepository.find(new DocumentId("some-id"))
document = user.approve(Document)
I would highly recommend reading Vaughn Vernon's three part aggregate design paper series better aggregete design

How should I persist CredentialPickerResults.Credential for CredentialPickerOptions.PreviousCredential?

I am wanting to use a CredentialPicker to prompt for a username and password. When I configure an instance of this class, I can set CredentialPickerOptions.PreviousCredential to a value previously obtained by CredentialPickerResults.Credential. I believe this causes the dialog to prepopulate the credentials.
However, persisting this value appears to be non-trivial; it's an IBuffer, whose members don't appear to contain the relevant credentials. Programming Windows 8 Apps with HTML, CSS, and JavaScript, page 657, implies that this should be possible:
An IBuffer containing the credential as an opaque byte array. This is what you can
save in your own persistent state if needs be and passed back to the picker at a later time; we’ll
see how shortly.
Unfortunately, the we'll see how shortly appears to only refer to the fact that the value can be passed back from memory into PreviousCredential; I didn't find any mention of how it's persisted.
Also, I want to persist the credentials using the recommended approach, which I believe is to use PasswordVault, however, this appears to only allow me to save the credentials as username and password strings rather than an IBuffer.
Thanks for taking the time to ask, and I certainly agree that I could've been more clear in that part of the book. Admittedly, I spent less time on Chapter 14 than I would have liked, but I'll try to remedy that in the next edition. Feedback like yours is extremely valuable in knowing where I need to make improvements, so I appreciate it.
Anyway, writing a buffer to a file is something that was mentioned back in Chapter 8 (and could've been mentioned again here...page 325, though it doesn't mention IBuffer explicitly). It's a straightforward job using the Windows.Storage.FileIO class as shown below (promise!).
First, a clarification. You have two ways to save the entered credentials. If you want to save the plain-text credentials, then absolutely use the Credential Locker. The benefit here is that those credentials can roam automatically with the user if that roaming passwords is enabled in PC Settings (it is by default). Otherwise, you can save the opaque CredentialPickerResults.credential property directly to a file. It's already encrypted and scrambled, so you don't need to use the credential locker in that case.
Now for saving/loading the credential property, which is an IBuffer. For this you use FileIO.writeBufferAsync to save and FileIO.readBufferAsync to reload.
I modified the Credential Picker sample, scenario 3 to demonstrate this. To save the credential property, I added this code at the end of the completed handler for pickAsync:
//results.credential will be null if the user cancels
if (results.credential != null) {
//Having retrieved a credential, write the opaque buffer to a file
var option = Windows.Storage.CreationCollisionOption.replaceExisting;
Windows.Storage.ApplicationData.current.localFolder.createFileAsync("credbuffer.dat", option).then(function (file) {
return Windows.Storage.FileIO.writeBufferAsync(file, results.credential);
}).done(function () {
//No results for this operation
console.log("credbuffer.dat written.");
}, function (e) {
console.log("Could not create credbuffer.dat file.");
});
}
Then I created a new function to load that credential, if possible. This is called on the Launch button click instead of launchCredPicker:
//In the page ready method:
document.getElementById("button1").addEventListener("click", readPrevCredentialAndLaunch, false);
//Added
function readPrevCredentialAndLaunch() {
Windows.Storage.ApplicationData.current.localFolder.getFileAsync("credbuffer.dat").then(function (file) {
return Windows.Storage.FileIO.readBufferAsync(file);
}).done(function (buffer) {
console.log("Read from credbuffer.dat");
launchCredPicker(buffer);
}, function (e) {
console.log("Could not reopen credbuffer.dat; launching default");
launchCredPicker(null);
});
}
//Modified to take a buffer
function launchCredPicker(prevCredBuffer) {
try {
var options = new Windows.Security.Credentials.UI.CredentialPickerOptions();
//Other options omitted
if (prevCredBuffer != null) {
options.previousCredential = prevCredBuffer;
}
//...
That's it. I put the modified JS sample on http://www.kraigbrockschmidt.com/src/CredentialPickerJS_modified.zip.
.Kraig
Author, Programming Windows 8 Apps in HTML, CSS, and JavaScript (free ebook)

Trouble Attaching File Programmatically to Email in Windows Metro App C#/XAML using Share Charm

I'm simply trying to attach a file named Document.pdf in the DocumentsLibrary to an email using the Share Charm. My code below works perfectly on the Local Machine:
private async void OnDataRequestedFiles(DataTransferManager sender, DataRequestedEventArgs e)
{
List<IStorageItem> shares = new List<IStorageItem>();
StorageFile filetoShare = await Windows.Storage.KnownFolders.DocumentsLibrary.GetFileAsync("Document.pdf");
if (filetoShare != null)
{
shares.Add(filetoShare);
filetoShare = null;
}
if (shares != null)
{
DataPackage requestData = e.Request.Data;
requestData.Properties.Title = "Title";
requestData.Properties.Description = "Description"; // The description is optional.
requestData.SetStorageItems(shares);
shares = null;
}
else
{
e.Request.FailWithDisplayText("File not Found.");
}
}
But when I run the exact same code on a Windows Surface Tablet, I get the dreaded "There's nothing to share right now." on the right in the Charms flyout area.
Here's a little more background to help:
I'm not looking to use a File Picker...I know the exact file I'm looking for
I've enabled the Documents Library Capability in the manifest
I've added a File Type Association for pdf in the manifest
and yes, the file does exist and is in the Documents Library
an email account is properly setup in the Mail App on the surface
I can successfully send text emails from the Tablet...just not emails with attachments
Like I said, this works on my Win 8 Development Machine as expected...just not on the Surface. I'm wondering if the Surface has different file or folder permissions?
Thanks for the help...this is driving me CRAZY
I finally figured it out - the problem was that my Event Handler was async (so that I could use await to set the StorageFile variable).
I solved it by setting the StorageFile variable earlier in my code so that it was already available when the Event Handler was called.
I still have no idea why it worked on my development machine, but no on the WinRT surface...
The handler can be an async method. In this case, it is critical to use DataTransferManager. Please refer to the MSDN page specifically for this scenario. For your convenience, the code from the page is copied to here:
private void RegisterForShare()
{
DataTransferManager dataTransferManager = DataTransferManager.GetForCurrentView();
dataTransferManager.DataRequested += new TypedEventHandler<DataTransferManager,
DataRequestedEventArgs>(this.ShareStorageItemsHandler);
}
private async void ShareStorageItemsHandler(DataTransferManager sender,
DataRequestedEventArgs e)
{
DataRequest request = e.Request;
request.Data.Properties.Title = "Share StorageItems Example";
request.Data.Properties.Description = "Demonstrates how to share files.";
// Because we are making async calls in the DataRequested event handler,
// we need to get the deferral first.
DataRequestDeferral deferral = request.GetDeferral();
// Make sure we always call Complete on the deferral.
try
{
StorageFile logoFile =
await Package.Current.InstalledLocation.GetFileAsync("Assets\\Logo.png");
List<IStorageItem> storageItems = new List<IStorageItem>();
storageItems.Add(logoFile);
request.Data.SetStorageItems(storageItems);
}
finally
{
deferral.Complete();
}
}
It is critical to place the following statement before any async method is called:
DataTransferManager dataTransferManager = DataTransferManager.GetForCurrentView();
You only have half a second to get the whole job done (getting the file, attaching...etc.). If the half-second deadline occurs you'll get this "driving crazy" message. Consider implementing some resumable logic and replace the message with "the attachment is being prepared please try again in a few seconds" (or else).
Your WinRT device might be just slower than your development machine. The latter just does the job before the deadline...