mvp file to ingress fn

This commit is contained in:
Per Stark
2024-09-25 14:20:19 +02:00
parent deac2ba0a3
commit 27cf09b81e
5 changed files with 70 additions and 13 deletions

View File

@@ -0,0 +1,33 @@
Your proposed structure for the API sounds solid and modular, making it easier to manage files and their relationships with other data. Heres a breakdown of how this can work and how it could be used with an iOS shortcut or a custom application:
API Structure
File Management Endpoints:
POST /file: Accepts a file upload and returns a unique identifier (ID) for that file.
PUT /file/{id}: Updates the metadata of the file identified by {id}.
DELETE /file/{id}: Deletes the file and its associated metadata from the database.
Data Ingress Endpoint:
POST /ingress: Accepts a JSON body containing references (IDs) to files and other necessary data, linking them in the database as needed.
Using with iOS Shortcuts
You can create shortcuts to interact with your API endpoints without the need for a full-fledged application. Here's how:
File Upload Shortcut:
Use the "Get File" action to select a file from the device.
Use the "Post" action to send a multipart/form-data request to the /file endpoint.
Parse the response to get the returned file ID.
Data Ingress Shortcut:
Use the "Ask for Input" action to gather the necessary fields for the ingress (like instructions, category, etc.) and the file ID(s).
Use another "Post" action to send this data to the /ingress endpoint as JSON.
Developing a CLI Tool
A CLI tool could also be developed for easier interaction with your API. This tool could:
Upload Files: Handle file uploads and return file IDs.
Link Data: Accept user input for instructions, category, and linked file IDs, then submit this data to the /ingress endpoint.
Additional Considerations
Error Handling: Ensure that both the upload and ingress endpoints handle errors gracefully and provide meaningful messages.
Documentation: Create clear API documentation to guide users in constructing requests correctly, whether they are using a shortcut, CLI, or custom application.
Authentication: Consider adding authentication to your API endpoints for security, especially if sensitive data is being handled.
This approach gives you the flexibility to support various clients, streamline interactions, and keep the server-side implementation clean and manageable. Would you like more specifics on any part of this setup?

View File

@@ -1,7 +1,12 @@
use std::hash::Hash;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use tracing::info;
use url::Url;
use uuid::Uuid;
use crate::redis::client::RedisClient;
use super::file_info::FileInfo;
#[derive(Debug, Deserialize, Serialize)]
@@ -48,7 +53,8 @@ pub enum IngressContentError {
impl IngressContent {
/// Create a new `IngressContent` from `IngressInput`.
pub async fn new(
input: IngressInput
input: IngressInput,
redis_client: &RedisClient, // Add RedisClient as a parameter
) -> Result<IngressContent, IngressContentError> {
let content = if let Some(input_content) = input.content {
// Check if the content is a URL
@@ -63,15 +69,26 @@ impl IngressContent {
None
};
// Check if there is files
// Use FileInfo.get(id) with all the ids in files vec
// Return a vec<FileInfo> as file_info_list
// Fetch file information if file UUIDs are provided
let files = if let Some(file_uuids) = input.files {
let mut file_info_list = Vec::new();
for uuid_str in file_uuids {
let uuid = Uuid::parse_str(&uuid_str).map_err(|_| IngressContentError::UnsupportedMime("Invalid UUID".into()))?;
match FileInfo::get(uuid, redis_client).await {
Ok(file_info) => file_info_list.push(file_info),
Err(_) => info!("No file with that uuid"),
}
}
Some(file_info_list)
} else {
None
};
Ok(IngressContent {
content,
instructions: input.instructions,
category: input.category,
files: None,
files,
})
}
}

View File

@@ -6,7 +6,6 @@ use crate::models::ingress::IngressContent;
use super::{RabbitMQCommon, RabbitMQConfig, RabbitMQError};
use tracing::{info, error};
use tokio::fs;
/// Struct to consume messages from RabbitMQ.
pub struct RabbitMQConsumer {
@@ -16,6 +15,16 @@ pub struct RabbitMQConsumer {
}
impl RabbitMQConsumer {
//// Creates a new 'RabbitMQConsumer' instance which sets up a rabbitmq client,
//// declares a exchange if needed, declares and binds a queue and initializes the consumer
////
//// # Arguments
////
//// * 'config' - RabbitMQConfig
////
//// # Returns
////
//// * 'Result<Self, RabbitMQError>' - The created client or an error.
pub async fn new(config: &RabbitMQConfig) -> Result<Self, RabbitMQError> {
let common = RabbitMQCommon::new(config).await?;
@@ -99,7 +108,7 @@ impl RabbitMQConsumer {
loop {
match self.consume().await {
Ok((ingress, delivery)) => {
info!("Received ingress object: {:?}", ingress);
// info!("Received ingress object: {:?}", ingress);
// Process the ingress object
self.handle_ingress_content(&ingress).await;

View File

@@ -1,5 +1,3 @@
// === Contents of ./routes/file.rs ===
use axum::{
extract::Path,
response::IntoResponse,
@@ -9,13 +7,12 @@ use axum::{
use axum_typed_multipart::{TypedMultipart, FieldData, TryFromMultipart};
use serde_json::json;
use tempfile::NamedTempFile;
use tracing::{error, info};
use tracing::info;
use uuid::Uuid;
use crate::{
models::file_info::{FileError, FileInfo},
redis::client::RedisClient,
rabbitmq::publisher::RabbitMQProducer,
};
#[derive(Debug, TryFromMultipart)]

View File

@@ -3,7 +3,7 @@ use std::sync::Arc;
use axum::{http::StatusCode, response::IntoResponse, Extension, Json};
use tracing::{error, info};
use crate::{models::ingress::{IngressContent, IngressInput}, rabbitmq::publisher::RabbitMQProducer};
use crate::{models::ingress::{IngressContent, IngressInput}, rabbitmq::publisher::RabbitMQProducer, redis::client::RedisClient};
pub async fn ingress_handler(
Extension(producer): Extension<Arc<RabbitMQProducer>>,
@@ -11,7 +11,8 @@ pub async fn ingress_handler(
) -> impl IntoResponse {
info!("Recieved input: {:?}", input);
if let Ok(content) = IngressContent::new(input).await {
let redis_client = RedisClient::new("redis://127.0.0.1/");
if let Ok(content) = IngressContent::new(input, &redis_client).await {
// Publish content to RabbitMQ (or other system)
match producer.publish(&content).await {