diff --git a/src/services/laconicService.ts b/src/services/laconicService.ts index 2e8d25c..84438e6 100644 --- a/src/services/laconicService.ts +++ b/src/services/laconicService.ts @@ -42,7 +42,7 @@ export async function publishAnimalRecord( description: string, imageUrl: string, portalName: string, - seiAddress: string // Changed parameter name from userId to seiAddress + seiAddress: string // Using seiAddress instead of userId ): Promise { try { // Create the record with required fields including seiAddress @@ -62,6 +62,7 @@ export async function publishAnimalRecord( } } + // This is the correct endpoint for publishing records const response = await axios.post('http://143.198.37.25:3000/publishRecord', { yamlContent: yaml.stringify(record) }, { @@ -71,6 +72,14 @@ export async function publishAnimalRecord( } }) + console.log('Successfully published animal record:', { + output: response.data.output, + mainObject, + location: { latitude, longitude }, + imageUrl: imageUrl.substring(0, 30) + '...', // Truncate for log readability + seiAddress + }); + return response.data.output } catch (error) { @@ -93,7 +102,7 @@ export async function publishAnimalRecord( } /** - * Query animal records by Sei wallet address + * Query animal records by Sei wallet address using GraphQL */ export async function getAnimalRecordsBySeiAddress(seiAddress: string): Promise { try { @@ -102,89 +111,201 @@ export async function getAnimalRecordsBySeiAddress(seiAddress: string): Promise< return []; } - // Query Laconic Registry for records with matching seiAddress - const response = await axios.post('http://143.198.37.25:3000/queryRecords', { - query: `record.seiAddress="${seiAddress}" AND record.type="AnimalRecord"` - }, { - headers: { - 'Authorization': `Bearer 1234`, // TODO: Use environment variable - 'Content-Type': 'application/json' + // Ensure GraphQL endpoint is defined + const LACONIC_GQL_ENDPOINT = process.env.NEXT_PUBLIC_LACONIC_GQL_ENDPOINT || 'https://laconicd-sapo.laconic.com/api'; + + // GraphQL query for records with matching seiAddress + const query = ` + query GetAnimalRecordsBySeiAddress($seiAddress: String!) { + queryRecords( + attributes: [ + { key: "type", value: { string: "AnimalRecord" } }, + { key: "seiAddress", value: { string: $seiAddress } } + ], + all: true + ) { + id + createTime + attributes { + key + value { + ... on StringValue { + string: value + } + ... on IntValue { + int: value + } + ... on FloatValue { + float: value + } + } + } + } } + `; + + // Send GraphQL request + const response = await fetch(LACONIC_GQL_ENDPOINT, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + query, + variables: { seiAddress } + }), + cache: 'no-cache' }); - // Parse and format the response - const records = response.data.results || []; - - // Map to a more usable format - return records.map(record => { - const data = record.record || {}; + const data = await response.json(); + + // Check for GraphQL errors + if (data.errors) { + console.error('GraphQL Errors:', data.errors); + return []; + } + + // Transform the response into our AnimalRecordResult format + const records = (data.data?.queryRecords || []).map((record: any) => { + // Convert attributes to a map + const attributesMap = record.attributes.reduce((acc: any, attr: any) => { + acc[attr.key] = attr.value.string || attr.value.int || attr.value.float; + return acc; + }, {}); + + // Safely parse location + let location = { latitude: 0, longitude: 0 }; + try { + if (attributesMap.location) { + if (typeof attributesMap.location === 'string') { + location = JSON.parse(attributesMap.location); + } else if (typeof attributesMap.location === 'object') { + location = attributesMap.location; + } + } + } catch (parseError) { + console.error('Error parsing location:', parseError); + } + return { - id: record.id || '', - mainObject: data.mainObject || '', - location: data.location || { latitude: 0, longitude: 0 }, - description: data.description || '', - imageUrl: data.imageUrl || '', - portalName: data.portalName || '', - seiAddress: data.seiAddress || '', - timestamp: data.timestamp || new Date().toISOString() + id: record.id, + mainObject: attributesMap.mainObject || 'Unknown', + location: { + latitude: location.latitude || 0, + longitude: location.longitude || 0 + }, + description: attributesMap.description || 'No description', + imageUrl: attributesMap.imageUrl || '', + portalName: attributesMap.portalName || '', + seiAddress: attributesMap.seiAddress || seiAddress, + timestamp: record.createTime || new Date().toISOString() }; - }).sort((a, b) => { - // Sort by timestamp descending (newest first) + }).filter((record: AnimalRecordResult) => record.imageUrl && record.imageUrl.trim() !== ''); + + // Sort by timestamp descending (newest first) + return records.sort((a, b) => { return new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime(); }); } catch (error) { - console.error('Failed to query animal records:', { - error: error.message, - status: axios.isAxiosError(error) ? error.response?.status : 'unknown', - data: axios.isAxiosError(error) ? error.response?.data : 'unknown' - }); - + console.error('Failed to query animal records by seiAddress:', error); return []; } } /** - * Get all animal records + * Get all animal records using GraphQL */ export async function getAllAnimalRecords(): Promise { try { - // Query Laconic Registry for all AnimalRecord type records - const response = await axios.post('http://143.198.37.25:3000/queryRecords', { - query: `record.type="AnimalRecord"` - }, { - headers: { - 'Authorization': `Bearer 1234`, // TODO: Use environment variable - 'Content-Type': 'application/json' + // Ensure GraphQL endpoint is defined + const LACONIC_GQL_ENDPOINT = process.env.NEXT_PUBLIC_LACONIC_GQL_ENDPOINT || 'https://laconicd-sapo.laconic.com/api'; + + // GraphQL query for all AnimalRecord records + const query = ` + query GetAllAnimalRecords { + queryRecords( + attributes: [ + { key: "type", value: { string: "AnimalRecord" } } + ], + all: true + ) { + id + createTime + attributes { + key + value { + ... on StringValue { + string: value + } + ... on IntValue { + int: value + } + ... on FloatValue { + float: value + } + } + } + } } + `; + + // Send GraphQL request + const response = await fetch(LACONIC_GQL_ENDPOINT, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ query }), + cache: 'no-cache' }); - // Parse and format the response - const records = response.data.results || []; - - // Map to a more usable format - return records.map(record => { - const data = record.record || {}; + const data = await response.json(); + + // Check for GraphQL errors + if (data.errors) { + console.error('GraphQL Errors:', data.errors); + return []; + } + + // Transform the response into our AnimalRecordResult format + const records = (data.data?.queryRecords || []).map((record: any) => { + // Convert attributes to a map + const attributesMap = record.attributes.reduce((acc: any, attr: any) => { + acc[attr.key] = attr.value.string || attr.value.int || attr.value.float; + return acc; + }, {}); + + // Safely parse location + let location = { latitude: 0, longitude: 0 }; + try { + if (attributesMap.location) { + if (typeof attributesMap.location === 'string') { + location = JSON.parse(attributesMap.location); + } else if (typeof attributesMap.location === 'object') { + location = attributesMap.location; + } + } + } catch (parseError) { + console.error('Error parsing location:', parseError); + } + return { - id: record.id || '', - mainObject: data.mainObject || '', - location: data.location || { latitude: 0, longitude: 0 }, - description: data.description || '', - imageUrl: data.imageUrl || '', - portalName: data.portalName || '', - seiAddress: data.seiAddress || '', - timestamp: data.timestamp || new Date().toISOString() + id: record.id, + mainObject: attributesMap.mainObject || 'Unknown', + location: { + latitude: location.latitude || 0, + longitude: location.longitude || 0 + }, + description: attributesMap.description || 'No description', + imageUrl: attributesMap.imageUrl || '', + portalName: attributesMap.portalName || '', + seiAddress: attributesMap.seiAddress || '', + timestamp: record.createTime || new Date().toISOString() }; - }).sort((a, b) => { - // Sort by timestamp descending (newest first) + }).filter((record: AnimalRecordResult) => record.imageUrl && record.imageUrl.trim() !== ''); + + // Sort by timestamp descending (newest first) + return records.sort((a, b) => { return new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime(); }); } catch (error) { - console.error('Failed to query all animal records:', { - error: error.message, - status: axios.isAxiosError(error) ? error.response?.status : 'unknown', - data: axios.isAxiosError(error) ? error.response?.data : 'unknown' - }); - + console.error('Failed to query all animal records:', error); return []; } } \ No newline at end of file