From e4fc2a95abc958b310409fd9174f6f051f261eaa Mon Sep 17 00:00:00 2001 From: Brian Pilati Date: Mon, 15 Sep 2025 16:08:05 -0500 Subject: [PATCH] fix(eng-8505): added config variable for google file picker --- docker-compose.yml | 1 + docker/scripts/check-config.js | 13 ++++++++++ package.json | 5 ++-- src/app/core/models/config.model.ts | 16 ++++++++++++ .../google-file-picker.component.spec.ts | 14 +++++++++++ .../google-file-picker.component.ts | 8 +++--- .../storage-item-selector.component.ts | 4 +-- src/app/shared/models/environment.model.ts | 6 ----- src/assets/config/template.json | 4 ++- src/environments/environment.development.ts | 25 ------------------- src/environments/environment.local.ts | 22 ---------------- src/environments/environment.test-osf.ts | 5 ---- src/environments/environment.ts | 9 ------- src/testing/mocks/environment.token.mock.ts | 4 --- 14 files changed, 56 insertions(+), 80 deletions(-) create mode 100644 docker/scripts/check-config.js diff --git a/docker-compose.yml b/docker-compose.yml index d5e025875..a8148f3b6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -22,6 +22,7 @@ services: - ./angular.json:/app/angular.json - ./tsconfig.json:/app/tsconfig.json - ./tsconfig.app.json:/app/tsconfig.app.json + - ./docker/scripts:/app/docker # (CMD comes from Dockerfile, but you could override here if you wanted) command: ['npm', 'run', 'start:docker'] diff --git a/docker/scripts/check-config.js b/docker/scripts/check-config.js new file mode 100644 index 000000000..6375bcd8a --- /dev/null +++ b/docker/scripts/check-config.js @@ -0,0 +1,13 @@ +const fs = require('fs'); +const path = require('path'); +const { config } = require('process'); + +const configPath = path.join(__dirname, '../src/assets/config/config.json'); +const templatePath = path.join(__dirname, '../src/assets/config/template.json'); + +if (!fs.existsSync(configPath)) { + console.log('[INFO] config.json not found. Copying from template.json...'); + fs.copyFileSync(templatePath, configPath); +} else { + console.log('[INFO] config.json already exists.'); +} diff --git a/package.json b/package.json index c523a00bb..b42736ec6 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "ng": "ng", "analyze-bundle": "ng build --configuration=analyze-bundle && source-map-explorer dist/**/*.js --no-border-checks", "build": "ng build", + "check:config": "node ./docker/check-config.js", "ci:test": "jest", "ci:test:coverage": "jest --coverage", "docs": "./node_modules/.bin/compodoc -p tsconfig.docs.json --name 'OSF Angular Documentation' --theme 'laravel' -s", @@ -19,8 +20,8 @@ "prepare": "husky", "start": "ng serve", "start:test": "ng serve --configuration test-osf", - "start:docker": "ng serve --host 0.0.0.0 --port 4200 --poll 2000", - "start:docker:local": "ng serve --host 0.0.0.0 --port 4200 --poll 2000 --configuration local", + "start:docker": "npm run check:config && ng serve --host 0.0.0.0 --port 4200 --poll 2000", + "start:docker:local": "npm run check:config && ng serve --host 0.0.0.0 --port 4200 --poll 2000 --configuration local", "test": "jest", "test:watch": "jest --watch", "test:coverage": "jest --coverage && npm run test:display", diff --git a/src/app/core/models/config.model.ts b/src/app/core/models/config.model.ts index e8e7d152a..de810b2ef 100644 --- a/src/app/core/models/config.model.ts +++ b/src/app/core/models/config.model.ts @@ -24,6 +24,22 @@ export interface ConfigModel { */ googleTagManagerId: string; + /** + * API Key used to load the Google Picker API. + * This key should be restricted in the Google Cloud Console to limit usage. + * + * @example "AIzaSyA...your_api_key" + */ + googleFilePickerApiKey: string; + + /** + * Google Cloud Project App ID used by the Google Picker SDK. + * This numeric ID identifies your Google project and is required for some configurations. + * + * @example 123456789012 + */ + googleFilePickerAppId: number; + /** * A catch-all for additional configuration keys not explicitly defined. * Each dynamic property maps to a `ConfigModelType` value. diff --git a/src/app/shared/components/addons/storage-item-selector/google-file-picker/google-file-picker.component.spec.ts b/src/app/shared/components/addons/storage-item-selector/google-file-picker/google-file-picker.component.spec.ts index ac8db2afc..578a1f54f 100644 --- a/src/app/shared/components/addons/storage-item-selector/google-file-picker/google-file-picker.component.spec.ts +++ b/src/app/shared/components/addons/storage-item-selector/google-file-picker/google-file-picker.component.spec.ts @@ -5,6 +5,7 @@ import { Observable, of, throwError } from 'rxjs'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { SENTRY_TOKEN } from '@core/factory/sentry.factory'; +import { OSFConfigService } from '@core/services/osf-config.service'; import { GoogleFilePickerDownloadService } from './service/google-file-picker.download.service'; import { GoogleFilePickerComponent } from './google-file-picker.component'; @@ -24,6 +25,17 @@ describe('Component: Google File Picker', () => { }), }; + const OSFConfigServiceProvider = { + provide: OSFConfigService, + useValue: { + get: (key: string) => { + if (key === 'googleFilePickerApiKey') return 'test-api-key'; + if (key === 'googleFilePickerAppId') return 'test-app-id'; + return null; + }, + }, + }; + let sentrySpy: any; let throwLoadScriptError = false; @@ -111,6 +123,7 @@ describe('Component: Google File Picker', () => { provide: Store, useValue: storeMock, }, + OSFConfigServiceProvider, ], }).compileComponents(); @@ -232,6 +245,7 @@ describe('Component: Google File Picker', () => { provide: Store, useValue: storeMock, }, + OSFConfigServiceProvider, ], }).compileComponents(); diff --git a/src/app/shared/components/addons/storage-item-selector/google-file-picker/google-file-picker.component.ts b/src/app/shared/components/addons/storage-item-selector/google-file-picker/google-file-picker.component.ts index 80b8e7f87..ac2272200 100644 --- a/src/app/shared/components/addons/storage-item-selector/google-file-picker/google-file-picker.component.ts +++ b/src/app/shared/components/addons/storage-item-selector/google-file-picker/google-file-picker.component.ts @@ -6,8 +6,8 @@ import { Button } from 'primeng/button'; import { ChangeDetectionStrategy, Component, inject, input, OnInit, signal } from '@angular/core'; -import { ENVIRONMENT } from '@core/constants/environment.token'; import { SENTRY_TOKEN } from '@core/factory/sentry.factory'; +import { OSFConfigService } from '@core/services/osf-config.service'; import { StorageItemModel } from '@osf/shared/models'; import { GoogleFileDataModel } from '@osf/shared/models/files/google-file.data.model'; import { GoogleFilePickerModel } from '@osf/shared/models/files/google-file.picker.model'; @@ -26,9 +26,9 @@ import { GoogleFilePickerDownloadService } from './service/google-file-picker.do }) export class GoogleFilePickerComponent implements OnInit { private readonly Sentry = inject(SENTRY_TOKEN); + private configService = inject(OSFConfigService); readonly #translateService = inject(TranslateService); readonly #googlePicker = inject(GoogleFilePickerDownloadService); - readonly #environment = inject(ENVIRONMENT); public isFolderPicker = input.required(); public rootFolder = input(null); @@ -39,8 +39,8 @@ export class GoogleFilePickerComponent implements OnInit { public accessToken = signal(null); public visible = signal(false); public isGFPDisabled = signal(true); - private readonly apiKey = this.#environment.google?.GOOGLE_FILE_PICKER_API_KEY ?? ''; - private readonly appId = this.#environment.google?.GOOGLE_FILE_PICKER_APP_ID ?? 0; + private readonly apiKey = this.configService.get('googleFilePickerApiKey'); + private readonly appId = this.configService.get('googleFilePickerAppId'); private readonly store = inject(Store); private parentId = ''; diff --git a/src/app/shared/components/addons/storage-item-selector/storage-item-selector.component.ts b/src/app/shared/components/addons/storage-item-selector/storage-item-selector.component.ts index 5ba74e824..b06065200 100644 --- a/src/app/shared/components/addons/storage-item-selector/storage-item-selector.component.ts +++ b/src/app/shared/components/addons/storage-item-selector/storage-item-selector.component.ts @@ -211,10 +211,10 @@ export class StorageItemSelectorComponent implements OnInit { this.cancelSelection.emit(); } - handleFolderSelection(folder: StorageItem): void { + handleFolderSelection = (folder: StorageItem): void => { this.selectedStorageItem.set(folder); this.hasFolderChanged.set(folder?.itemId !== this.initiallySelectedStorageItem()?.itemId); - } + }; private updateBreadcrumbs( operationName: OperationNames, diff --git a/src/app/shared/models/environment.model.ts b/src/app/shared/models/environment.model.ts index 7671308db..024e42559 100644 --- a/src/app/shared/models/environment.model.ts +++ b/src/app/shared/models/environment.model.ts @@ -15,12 +15,6 @@ export interface AppEnvironment { dataciteTrackerRepoId: string | null; dataciteTrackerAddress: string; - google?: { - GOOGLE_FILE_PICKER_CLIENT_ID: string; - GOOGLE_FILE_PICKER_API_KEY: string; - GOOGLE_FILE_PICKER_APP_ID: number; - }; - activityLogs?: { pageSize?: number; }; diff --git a/src/assets/config/template.json b/src/assets/config/template.json index d407164e3..ab4553775 100644 --- a/src/assets/config/template.json +++ b/src/assets/config/template.json @@ -1,4 +1,6 @@ { "sentryDsn": "", - "googleTagManagerId": "" + "googleTagManagerId": "", + "googleFilePickerApiKey": "", + "googleFilePickerAppId": 0 } diff --git a/src/environments/environment.development.ts b/src/environments/environment.development.ts index 5138269ee..4ddc9b332 100644 --- a/src/environments/environment.development.ts +++ b/src/environments/environment.development.ts @@ -56,29 +56,4 @@ export const environment = { defaultProvider: 'osf', dataciteTrackerRepoId: null, dataciteTrackerAddress: 'https://analytics.datacite.org/api/metric', - /** - * Google File Picker configuration values. - */ - google: { - /** - * OAuth 2.0 Client ID used to identify the application during Google authentication. - * Registered in Google Cloud Console under "OAuth 2.0 Client IDs". - * Safe to expose in frontend code. - * @see https://console.cloud.google.com/apis/credentials - */ - GOOGLE_FILE_PICKER_CLIENT_ID: '610901277352-m5krehjdtu8skh2teq85fb7mvk411qa6.apps.googleusercontent.com', - /** - * Public API key used to load Google Picker and other Google APIs that don’t require user auth. - * Must be restricted by referrer in Google Cloud Console. - * Exposing this key is acceptable if restricted properly. - * @see https://developers.google.com/maps/api-key-best-practices - */ - GOOGLE_FILE_PICKER_API_KEY: 'AIzaSyA3EnD0pOv4v7sJt7BGuR1i2Gcj-Gju6C0', - /** - * Google Cloud Project App ID. - * Used for associating API requests with the specific Google project. - * Required for Google Picker configuration. - */ - GOOGLE_FILE_PICKER_APP_ID: 610901277352, - }, }; diff --git a/src/environments/environment.local.ts b/src/environments/environment.local.ts index 342dc0d2c..05ff0cb9e 100644 --- a/src/environments/environment.local.ts +++ b/src/environments/environment.local.ts @@ -14,26 +14,4 @@ export const environment = { defaultProvider: 'osf', dataciteTrackerRepoId: null, dataciteTrackerAddress: 'https://analytics.datacite.org/api/metric', - google: { - /** - * OAuth 2.0 Client ID used to identify the application during Google authentication. - * Registered in Google Cloud Console under "OAuth 2.0 Client IDs". - * Safe to expose in frontend code. - * @see https://console.cloud.google.com/apis/credentials - */ - GOOGLE_FILE_PICKER_CLIENT_ID: '610901277352-m5krehjdtu8skh2teq85fb7mvk411qa6.apps.googleusercontent.com', - /** - * Public API key used to load Google Picker and other Google APIs that don’t require user auth. - * Must be restricted by referrer in Google Cloud Console. - * Exposing this key is acceptable if restricted properly. - * @see https://developers.google.com/maps/api-key-best-practices - */ - GOOGLE_FILE_PICKER_API_KEY: 'AIzaSyA3EnD0pOv4v7sJt7BGuR1i2Gcj-Gju6C0', - /** - * Google Cloud Project App ID. - * Used for associating API requests with the specific Google project. - * Required for Google Picker configuration. - */ - GOOGLE_FILE_PICKER_APP_ID: 610901277352, - }, }; diff --git a/src/environments/environment.test-osf.ts b/src/environments/environment.test-osf.ts index 19fed8086..eaa045a52 100644 --- a/src/environments/environment.test-osf.ts +++ b/src/environments/environment.test-osf.ts @@ -16,9 +16,4 @@ export const environment = { defaultProvider: 'osf', dataciteTrackerRepoId: null, dataciteTrackerAddress: 'https://analytics.datacite.org/api/metric', - google: { - GOOGLE_FILE_PICKER_CLIENT_ID: '610901277352-m5krehjdtu8skh2teq85fb7mvk411qa6.apps.googleusercontent.com', - GOOGLE_FILE_PICKER_API_KEY: 'AIzaSyA3EnD0pOv4v7sJt7BGuR1i2Gcj-Gju6C0', - GOOGLE_FILE_PICKER_APP_ID: 610901277352, - }, }; diff --git a/src/environments/environment.ts b/src/environments/environment.ts index 1f58cbc79..8473fe0a9 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -56,13 +56,4 @@ export const environment = { defaultProvider: 'osf', dataciteTrackerRepoId: null, dataciteTrackerAddress: 'https://analytics.datacite.org/api/metric', - - /** - * Google File Picker configuration values. - */ - google: { - GOOGLE_FILE_PICKER_CLIENT_ID: '610901277352-m5krehjdtu8skh2teq85fb7mvk411qa6.apps.googleusercontent.com', - GOOGLE_FILE_PICKER_API_KEY: 'AIzaSyA3EnD0pOv4v7sJt7BGuR1i2Gcj-Gju6C0', - GOOGLE_FILE_PICKER_APP_ID: 610901277352, - }, }; diff --git a/src/testing/mocks/environment.token.mock.ts b/src/testing/mocks/environment.token.mock.ts index be12d7e14..7e95f9b68 100644 --- a/src/testing/mocks/environment.token.mock.ts +++ b/src/testing/mocks/environment.token.mock.ts @@ -24,9 +24,5 @@ export const EnvironmentTokenMock = { provide: ENVIRONMENT, useValue: { production: false, - google: { - GOOGLE_FILE_PICKER_API_KEY: 'test-api-key', - GOOGLE_FILE_PICKER_APP_ID: 'test-app-id', - }, }, };