An easy-to-host PDS on the ATProtocol, iPhone and MacOS. Maintain control of your keys and data, always.
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(identity-wallet): add post_plc_operation to PdsClient

Adds method to submit signed PLC operations to plc.directory.
- POST {plc_directory_url}/{did} with signed operation JSON body
- Returns Ok(()) on 2xx, InvalidResponse with body text on error
- Includes 2 tests: success (200) and failure (409)

authored by

Malpercio and committed by
Tangled
8f692b71 021dae89

+81
+81
apps/identity-wallet/src-tauri/src/pds_client.rs
··· 473 473 message: format!("failed to read audit log response: {}", e), 474 474 }) 475 475 } 476 + 477 + /// Submit a signed PLC operation to plc.directory. 478 + /// 479 + /// Calls `POST {plc_directory_url}/{did}` with the signed operation as JSON body. 480 + pub async fn post_plc_operation( 481 + &self, 482 + did: &str, 483 + operation: &serde_json::Value, 484 + ) -> Result<(), PdsClientError> { 485 + let url = format!("{}/{}", self.plc_directory_url, did); 486 + let resp = self 487 + .client 488 + .post(&url) 489 + .json(operation) 490 + .send() 491 + .await 492 + .map_err(|e| PdsClientError::NetworkError { 493 + message: format!("failed to post plc operation: {}", e), 494 + })?; 495 + 496 + if resp.status().is_success() { 497 + Ok(()) 498 + } else { 499 + let body = resp.text().await.unwrap_or_default(); 500 + Err(PdsClientError::InvalidResponse { 501 + message: format!("plc.directory rejected operation: {}", body), 502 + }) 503 + } 504 + } 476 505 } 477 506 478 507 impl Default for PdsClient { ··· 1853 1882 // Expected 1854 1883 } 1855 1884 e => panic!("Expected DidNotFound, got: {:?}", e), 1885 + } 1886 + } 1887 + 1888 + // ============================================================================ 1889 + // post_plc_operation tests 1890 + // ============================================================================ 1891 + 1892 + /// post_plc_operation succeeds with 200 response 1893 + #[tokio::test] 1894 + async fn test_post_plc_operation_success() { 1895 + let mock_server = MockServer::start(); 1896 + 1897 + mock_server.mock(|when, then| { 1898 + when.method(httpmock::Method::POST).path("/did:plc:test123"); 1899 + then.status(200); 1900 + }); 1901 + 1902 + let client = PdsClient::new_for_test(mock_server.base_url()); 1903 + let operation = serde_json::json!({ 1904 + "type": "plc_operation", 1905 + "prev": "bafy123", 1906 + "rotationKeys": ["did:key:z123"] 1907 + }); 1908 + 1909 + let result = client.post_plc_operation("did:plc:test123", &operation).await; 1910 + 1911 + assert!(result.is_ok()); 1912 + } 1913 + 1914 + /// post_plc_operation returns InvalidResponse with error body on non-2xx 1915 + #[tokio::test] 1916 + async fn test_post_plc_operation_conflict() { 1917 + let mock_server = MockServer::start(); 1918 + 1919 + mock_server.mock(|when, then| { 1920 + when.method(httpmock::Method::POST).path("/did:plc:test123"); 1921 + then.status(409).body("Conflicting operation"); 1922 + }); 1923 + 1924 + let client = PdsClient::new_for_test(mock_server.base_url()); 1925 + let operation = serde_json::json!({ 1926 + "type": "plc_operation" 1927 + }); 1928 + 1929 + let result = client.post_plc_operation("did:plc:test123", &operation).await; 1930 + 1931 + assert!(result.is_err()); 1932 + match result.unwrap_err() { 1933 + PdsClientError::InvalidResponse { message } => { 1934 + assert!(message.contains("Conflicting operation")); 1935 + } 1936 + e => panic!("Expected InvalidResponse, got: {:?}", e), 1856 1937 } 1857 1938 } 1858 1939 }