Skip to content

Commit abd563d

Browse files
authored
[ENG-9260] | fix(google-drive): resolve file list refresh and intermittent 403 errors #839
## Purpose PR Summary: Problem: After configuring Google Drive addon, two issues occurred: 1. Files added via "Add from Drive" button didn't appear immediately - required page reload (F5) 2. Intermittent 403 "Forbidden" errors when opening Google Drive picker Solution: - Fixed updateFilesList callback losing this context by converting it to an arrow function property - OAuth token now refreshes each time the picker opens instead of only once during component initialization Result: Files now appear immediately after selection, and 403 errors should be eliminated since a fresh token is fetched before each picker open.
1 parent 81177e6 commit abd563d

File tree

3 files changed

+116
-2
lines changed

3 files changed

+116
-2
lines changed

src/app/features/files/pages/files/files.component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -596,13 +596,13 @@ export class FilesComponent {
596596
});
597597
}
598598

599-
updateFilesList() {
599+
updateFilesList = (): void => {
600600
const currentFolder = this.currentFolder();
601601
const filesLink = currentFolder?.links.filesLink;
602602
if (filesLink) {
603603
this.actions.getFiles(filesLink, this.pageNumber());
604604
}
605-
}
605+
};
606606

607607
setCurrentFolder(folder: FileFolderModel) {
608608
this.actions.setCurrentFolder(folder);

src/app/shared/components/google-file-picker/google-file-picker.component.spec.ts

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Observable, of, throwError } from 'rxjs';
44

55
import { ComponentFixture, TestBed } from '@angular/core/testing';
66

7+
import { ENVIRONMENT } from '@core/provider/environment.provider';
78
import { SENTRY_TOKEN } from '@core/provider/sentry.provider';
89
import { GoogleFilePickerDownloadService } from '@osf/shared/services/google-file-picker.download.service';
910

@@ -296,5 +297,93 @@ describe('Component: Google File Picker', () => {
296297
expect(build).toHaveBeenCalledWith();
297298
expect(setVisible).toHaveBeenCalledWith(true);
298299
});
300+
301+
it('should open picker with current token when oauth refresh fails', () => {
302+
const errorStoreMock = {
303+
dispatch: jest.fn().mockReturnValue(throwError(() => new Error('OAuth refresh failed'))),
304+
selectSnapshot: jest.fn().mockReturnValue(null),
305+
};
306+
307+
TestBed.resetTestingModule();
308+
TestBed.configureTestingModule({
309+
imports: [OSFTestingStoreModule, GoogleFilePickerComponent],
310+
providers: [
311+
{ provide: SENTRY_TOKEN, useValue: { captureException: jest.fn() } },
312+
{ provide: GoogleFilePickerDownloadService, useValue: googlePickerServiceSpy },
313+
{ provide: Store, useValue: errorStoreMock },
314+
],
315+
}).compileComponents();
316+
317+
fixture = TestBed.createComponent(GoogleFilePickerComponent);
318+
component = fixture.componentInstance;
319+
fixture.componentRef.setInput('isFolderPicker', false);
320+
fixture.componentRef.setInput('rootFolder', { itemId: 'root-folder-id' });
321+
fixture.componentRef.setInput('handleFolderSelection', jest.fn());
322+
fixture.componentRef.setInput('accountId', 'account-id');
323+
fixture.detectChanges();
324+
325+
jest.clearAllMocks();
326+
component.createPicker();
327+
328+
expect(errorStoreMock.dispatch).toHaveBeenCalled();
329+
expect(setVisible).toHaveBeenCalledWith(true);
330+
});
331+
});
332+
333+
describe('picker not configured', () => {
334+
it('should disable picker when apiKey or appId is missing', async () => {
335+
await TestBed.configureTestingModule({
336+
imports: [OSFTestingModule, GoogleFilePickerComponent],
337+
providers: [
338+
{ provide: SENTRY_TOKEN, useValue: { captureException: jest.fn() } },
339+
{ provide: GoogleFilePickerDownloadService, useValue: googlePickerServiceSpy },
340+
{ provide: Store, useValue: storeMock },
341+
],
342+
})
343+
.overrideProvider(ENVIRONMENT, {
344+
useValue: {
345+
googleFilePickerApiKey: '',
346+
googleFilePickerAppId: '',
347+
},
348+
})
349+
.compileComponents();
350+
351+
fixture = TestBed.createComponent(GoogleFilePickerComponent);
352+
component = fixture.componentInstance;
353+
fixture.componentRef.setInput('isFolderPicker', true);
354+
fixture.detectChanges();
355+
356+
expect(component.isGFPDisabled()).toBeTruthy();
357+
expect(googlePickerServiceSpy.loadScript).not.toHaveBeenCalled();
358+
});
359+
360+
it('should not open picker when not configured', async () => {
361+
await TestBed.configureTestingModule({
362+
imports: [OSFTestingModule, GoogleFilePickerComponent],
363+
providers: [
364+
{ provide: SENTRY_TOKEN, useValue: { captureException: jest.fn() } },
365+
{ provide: GoogleFilePickerDownloadService, useValue: googlePickerServiceSpy },
366+
{ provide: Store, useValue: storeMock },
367+
],
368+
})
369+
.overrideProvider(ENVIRONMENT, {
370+
useValue: {
371+
googleFilePickerApiKey: '',
372+
googleFilePickerAppId: '',
373+
},
374+
})
375+
.compileComponents();
376+
377+
fixture = TestBed.createComponent(GoogleFilePickerComponent);
378+
component = fixture.componentInstance;
379+
fixture.componentRef.setInput('isFolderPicker', true);
380+
fixture.detectChanges();
381+
382+
jest.clearAllMocks();
383+
component.createPicker();
384+
385+
expect(build).not.toHaveBeenCalled();
386+
expect(setVisible).not.toHaveBeenCalled();
387+
});
299388
});
300389
});

src/app/shared/components/google-file-picker/google-file-picker.component.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,31 @@ export class GoogleFilePickerComponent implements OnInit {
8888

8989
createPicker(): void {
9090
if (!this.isPickerConfigured) return;
91+
92+
this.refreshOauthTokenAndOpenPicker();
93+
}
94+
95+
private refreshOauthTokenAndOpenPicker(): void {
96+
if (!this.accountId()) {
97+
this.openPickerWithCurrentToken();
98+
return;
99+
}
100+
101+
this.store.dispatch(new GetAuthorizedStorageOauthToken(this.accountId(), this.currentAddonType())).subscribe({
102+
complete: () => {
103+
this.accessToken.set(
104+
this.store.selectSnapshot(AddonsSelectors.getAuthorizedStorageAddonOauthToken(this.accountId()))
105+
);
106+
this.isGFPDisabled.set(!this.accessToken());
107+
this.openPickerWithCurrentToken();
108+
},
109+
error: () => {
110+
this.openPickerWithCurrentToken();
111+
},
112+
});
113+
}
114+
115+
private openPickerWithCurrentToken(): void {
91116
const google = window.google;
92117

93118
const googlePickerView = new google.picker.DocsView(google.picker.ViewId.DOCS);

0 commit comments

Comments
 (0)