feat: add oauth with remote server (#1178)

This commit is contained in:
Ralph Khreish
2025-09-04 20:45:41 +02:00
parent 0f3ab00f26
commit 7cf4004038
37 changed files with 2340 additions and 7147 deletions

View File

@@ -177,7 +177,7 @@ describe('ConfigManager', () => {
it('should return storage configuration', () => {
const storage = manager.getStorageConfig();
expect(storage).toEqual({ type: 'file' });
expect(storage).toEqual({ type: 'auto', apiConfigured: false });
});
it('should return API storage configuration when configured', async () => {
@@ -206,7 +206,65 @@ describe('ConfigManager', () => {
expect(storage).toEqual({
type: 'api',
apiEndpoint: 'https://api.example.com',
apiAccessToken: 'token123'
apiAccessToken: 'token123',
apiConfigured: true
});
});
it('should return auto storage configuration with apiConfigured flag', async () => {
// Create a new instance with auto storage config and partial API settings
vi.mocked(ConfigMerger).mockImplementationOnce(
() =>
({
addSource: vi.fn(),
clearSources: vi.fn(),
merge: vi.fn().mockReturnValue({
storage: {
type: 'auto',
apiEndpoint: 'https://api.example.com'
// No apiAccessToken - partial config
}
}),
getSources: vi.fn().mockReturnValue([])
}) as any
);
const autoManager = await ConfigManager.create(testProjectRoot);
const storage = autoManager.getStorageConfig();
expect(storage).toEqual({
type: 'auto',
apiEndpoint: 'https://api.example.com',
apiAccessToken: undefined,
apiConfigured: true // true because apiEndpoint is provided
});
});
it('should return auto storage with apiConfigured false when no API settings', async () => {
// Create a new instance with auto storage but no API settings
vi.mocked(ConfigMerger).mockImplementationOnce(
() =>
({
addSource: vi.fn(),
clearSources: vi.fn(),
merge: vi.fn().mockReturnValue({
storage: {
type: 'auto'
// No API settings at all
}
}),
getSources: vi.fn().mockReturnValue([])
}) as any
);
const autoManager = await ConfigManager.create(testProjectRoot);
const storage = autoManager.getStorageConfig();
expect(storage).toEqual({
type: 'auto',
apiEndpoint: undefined,
apiAccessToken: undefined,
apiConfigured: false // false because no API settings
});
});

View File

@@ -135,25 +135,26 @@ export class ConfigManager {
* Get storage configuration
*/
getStorageConfig(): {
type: 'file' | 'api';
type: 'file' | 'api' | 'auto';
apiEndpoint?: string;
apiAccessToken?: string;
apiConfigured: boolean;
} {
const storage = this.config.storage;
if (
storage?.type === 'api' &&
storage.apiEndpoint &&
storage.apiAccessToken
) {
// Return the configured type (including 'auto')
const storageType = storage?.type || 'auto';
if (storageType === 'api' || storageType === 'auto') {
return {
type: 'api',
apiEndpoint: storage.apiEndpoint,
apiAccessToken: storage.apiAccessToken
type: storageType,
apiEndpoint: storage?.apiEndpoint,
apiAccessToken: storage?.apiAccessToken,
apiConfigured: Boolean(storage?.apiEndpoint || storage?.apiAccessToken)
};
}
return { type: 'file' };
return { type: storageType, apiConfigured: false };
}
/**
@@ -269,12 +270,4 @@ export class ConfigManager {
getConfigSources() {
return this.merger.getSources();
}
/**
* Watch for configuration changes (placeholder for future)
*/
watch(_callback: (config: PartialConfiguration) => void): () => void {
console.warn('Configuration watching not yet implemented');
return () => {}; // Return no-op unsubscribe function
}
}

View File

@@ -85,6 +85,11 @@ describe('EnvironmentConfigProvider', () => {
provider = new EnvironmentConfigProvider(); // Reset provider
config = provider.loadConfig();
expect(config.storage?.type).toBe('api');
process.env.TASKMASTER_STORAGE_TYPE = 'auto';
provider = new EnvironmentConfigProvider(); // Reset provider
config = provider.loadConfig();
expect(config.storage?.type).toBe('auto');
});
it('should handle nested configuration paths', () => {

View File

@@ -31,7 +31,7 @@ export class EnvironmentConfigProvider {
{
env: 'TASKMASTER_STORAGE_TYPE',
path: ['storage', 'type'],
validate: (v: string) => ['file', 'api'].includes(v)
validate: (v: string) => ['file', 'api', 'auto'].includes(v)
},
{ env: 'TASKMASTER_API_ENDPOINT', path: ['storage', 'apiEndpoint'] },
{ env: 'TASKMASTER_API_TOKEN', path: ['storage', 'apiAccessToken'] },