Cloud Sync Examples

Ecosistema completo para organización y gestión de contenido con interfaces espaciales

Cloud Sync Examples

Our storage system provides seamless integration with cloud services and REST APIs. This guide shows practical examples of syncing data with Firestore and REST APIs using our @mock-api integration.

🎮 Live Demo Components

🔥 Firestore Integration

Setup & Configuration

// app.config.ts  
import { provideGgStorage } from '@storage/angular';
import { FirestoreSyncAdapter, FIRESTORE_SYNC_CONFIG } from '@storage/sync';

providers: [
  ...provideGgStorage({
    namespace: 'myapp',
    sync: {
      adapter: new FirestoreSyncAdapter({
        projectId: 'your-firebase-project',
        collection: 'storage-sync',
        apiKey: 'your-api-key',
        authDomain: 'your-project.firebaseapp.com'
      })
    }
  }),
  
  // Or provide configuration separately
  {
    provide: FIRESTORE_SYNC_CONFIG,
    useValue: {
      projectId: 'your-firebase-project',
      collection: 'storage-sync',
      apiKey: 'your-api-key'
    }
  }
]

Basic Sync Operations

// component.ts
import { inject } from '@angular/core';
import { StorageService } from '@storage/angular';

const storage = inject(StorageService);

// Save locally and sync to Firestore automatically
await storage.put('user/profile', {
  name: 'John Doe',
  email: '[email protected]',
  preferences: { theme: 'dark' }
});

// Data is automatically pushed to Firestore
// Firestore document: /storage-sync/user%2Fprofile

Manual Sync Control

// Get the Firestore adapter for manual operations
const firestoreAdapter = storage.getSyncAdapter() as FirestoreSyncAdapter;

// Push specific changes
const changes = [{
  key: 'user/settings',
  op: 'put' as const,
  value: { notifications: true },
  updatedAt: Date.now()
}];

await firestoreAdapter.push(changes);

// Pull remote changes
const status = await firestoreAdapter.getStatus();
const remoteChanges = await firestoreAdapter.pull(status.cursor);

// Apply remote changes locally
for (const change of remoteChanges) {
  if (change.op === 'put') {
    await storage.put(change.key, change.value);
  } else if (change.op === 'remove') {
    await storage.remove(change.key);
  }
}

Firestore Security Rules

// firestore.rules
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // Storage sync collection
    match /storage-sync/{document} {
      allow read, write: if request.auth != null;
    }
    
    // User-specific data
    match /storage-sync/{userId}/{document=**} {
      allow read, write: if request.auth != null && request.auth.uid == userId;
    }
  }
}

🌐 REST API Integration

Setup with Mock API

// app.config.ts
import { provideGgStorage } from '@storage/angular';
import { ApiSyncAdapter, API_SYNC_CONFIG } from '@storage/sync';

providers: [
  ...provideGgStorage({
    namespace: 'myapp',
    sync: {
      adapter: new ApiSyncAdapter({
        baseUrl: 'http://localhost:3000/api',
        endpoint: '/storage/sync',
        apiKey: 'your-api-key'
      })
    }
  })
]

Mock API Endpoints

// mock-api/storage.mock.ts
import { MockApiHandler } from '@mock-api/core';

export const storageMockApi: MockApiHandler[] = [
  // Get sync status and changes
  {
    method: 'GET',
    path: '/api/storage/sync',
    handler: (req) => {
      const cursor = req.query.cursor || '0';
      return {
        cursor: Date.now().toString(),
        changes: [
          {
            key: 'user/profile',
            op: 'put',
            value: { name: 'John', updated: true },
            updatedAt: Date.now()
          }
        ]
      };
    }
  },

  // Push changes to server
  {
    method: 'POST',
    path: '/api/storage/sync',
    handler: (req) => {
      const changes = req.body.changes;
      console.log('Received changes:', changes);
      
      // Store changes in mock database
      changes.forEach(change => {
        mockDatabase.set(change.key, change);
      });
      
      return {
        success: true,
        cursor: Date.now().toString(),
        processed: changes.length
      };
    }
  },

  // Get specific data by key
  {
    method: 'GET',
    path: '/api/storage/data/:key',
    handler: (req) => {
      const key = decodeURIComponent(req.params.key);
      const data = mockDatabase.get(key);
      
      if (!data) {
        return { error: 'Not found' };
      }
      
      return {
        key,
        value: data.value,
        updatedAt: data.updatedAt
      };
    }
  },

  // Update specific data
  {
    method: 'PUT',
    path: '/api/storage/data/:key',
    handler: (req) => {
      const key = decodeURIComponent(req.params.key);
      const { value } = req.body;
      
      mockDatabase.set(key, {
        key,
        op: 'put',
        value,
        updatedAt: Date.now()
      });
      
      return {
        success: true,
        key,
        updatedAt: Date.now()
      };
    }
  }
];

// Simple in-memory mock database
const mockDatabase = new Map();

API Sync Operations

// component.ts
const storage = inject(StorageService);
const apiAdapter = storage.getSyncAdapter() as ApiSyncAdapter;

// Automatic sync on data changes
await storage.put('user/settings', {
  theme: 'dark',
  language: 'en',
  notifications: true
});
// Automatically synced to: POST /api/storage/sync

// Manual bidirectional sync
async function performSync() {
  try {
    // Get current sync status
    const status = await apiAdapter.getStatus();
    console.log('Current cursor:', status.cursor);
    
    // Pull remote changes first
    const remoteChanges = await apiAdapter.pull(status.cursor);
    console.log('Pulled changes:', remoteChanges);
    
    // Apply remote changes locally
    for (const change of remoteChanges) {
      if (change.op === 'put') {
        await storage.put(change.key, change.value);
      } else if (change.op === 'remove') {
        await storage.remove(change.key);
      }
    }
    
    // Push any local changes
    const localChanges = await getLocalChanges(); // Your implementation
    if (localChanges.length > 0) {
      await apiAdapter.push(localChanges);
    }
    
    console.log('Sync completed successfully');
  } catch (error) {
    console.error('Sync failed:', error);
  }
}

🔄 Conflict Resolution

Last-Write-Wins Strategy

// Simple conflict resolution
const apiAdapter = new ApiSyncAdapter({
  baseUrl: 'http://localhost:3000/api',
  endpoint: '/storage/sync',
  conflictResolution: 'last-write-wins' // Default
});

Custom Conflict Resolution

// Advanced conflict resolution
const apiAdapter = new ApiSyncAdapter({
  baseUrl: 'http://localhost:3000/api',
  endpoint: '/storage/sync',
  conflictResolution: async (local, remote) => {
    // Custom logic for resolving conflicts
    if (local.key.startsWith('user/')) {
      // User data: prefer local changes
      return local;
    } else if (remote.updatedAt > local.updatedAt) {
      // General data: prefer newer timestamp
      return remote;
    } else {
      return local;
    }
  }
});

📊 Monitoring & Debugging

Sync Status Monitoring

// Monitor sync status
const adapter = storage.getSyncAdapter();

setInterval(async () => {
  const status = await adapter.getStatus();
  console.log('Sync Status:', {
    cursor: status.cursor,
    lastSync: new Date(status.lastSync),
    pendingChanges: status.pendingChanges
  });
}, 30000); // Check every 30 seconds

Error Handling

// Robust error handling
try {
  await storage.put('critical/data', importantData);
} catch (error) {
  if (error.code === 'SYNC_FAILED') {
    // Handle sync failure
    console.warn('Sync failed, data saved locally:', error.message);
    
    // Retry sync later
    setTimeout(() => {
      storage.retrySync();
    }, 60000);
  } else {
    // Handle other errors
    console.error('Storage error:', error);
  }
}

🚀 Performance Optimization

Batch Operations

// Efficient batch syncing
const changes = [
  { key: 'user/profile', op: 'put', value: profile, updatedAt: Date.now() },
  { key: 'user/settings', op: 'put', value: settings, updatedAt: Date.now() },
  { key: 'temp/old-data', op: 'remove', updatedAt: Date.now() }
];

await apiAdapter.push(changes); // Single API call

Selective Sync

// Sync only specific data patterns
const apiAdapter = new ApiSyncAdapter({
  baseUrl: 'http://localhost:3000/api',
  endpoint: '/storage/sync',
  syncFilter: (key) => {
    // Only sync user data and settings
    return key.startsWith('user/') || key.startsWith('settings/');
  }
});

🔐 Security Best Practices

API Authentication

// JWT token authentication
const apiAdapter = new ApiSyncAdapter({
  baseUrl: 'https://api.myapp.com',
  endpoint: '/storage/sync',
  headers: {
    'Authorization': `Bearer ${getAuthToken()}`,
    'Content-Type': 'application/json'
  }
});

// Refresh token automatically
apiAdapter.onAuthError = async () => {
  const newToken = await refreshAuthToken();
  apiAdapter.updateHeaders({
    'Authorization': `Bearer ${newToken}`
  });
};

Data Encryption

// Client-side encryption before sync
const apiAdapter = new ApiSyncAdapter({
  baseUrl: 'https://api.myapp.com',
  endpoint: '/storage/sync',
  encrypt: (data) => encryptData(data, userKey),
  decrypt: (data) => decryptData(data, userKey)
});

🧪 Testing with Mock API

Development Setup

// development.config.ts
import { storageMockApi } from './mock-api/storage.mock';

providers: [
  ...provideMockApi(storageMockApi), // Enable mock API
  ...provideGgStorage({
    namespace: 'dev',
    sync: {
      adapter: new ApiSyncAdapter({
        baseUrl: 'http://localhost:4200/api', // Mock API URL
        endpoint: '/storage/sync'
      })
    }
  })
]

Integration Tests

// storage-sync.spec.ts
describe('Storage Sync', () => {
  let storage: StorageService;
  let mockApi: MockApiService;

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [
        ...provideMockApi(storageMockApi),
        ...provideGgStorage({
          namespace: 'test',
          sync: { adapter: new ApiSyncAdapter({...}) }
        })
      ]
    });
    
    storage = TestBed.inject(StorageService);
    mockApi = TestBed.inject(MockApiService);
  });

  it('should sync data to API', async () => {
    // Setup mock response
    mockApi.expectPost('/api/storage/sync').respond({
      success: true,
      processed: 1
    });

    // Perform operation
    await storage.put('test/data', { value: 'test' });

    // Verify API was called
    mockApi.verify();
  });
});

This comprehensive cloud sync integration enables seamless data synchronization across devices and platforms while maintaining the intelligent, performance-focused approach of our storage system.