I am new to SST, I tried to create an upload API + Lambda to upload files and set metadata and it worked. Now I am trying to create an API + Lambda to retrieve the list of files. I was able to retrieve the full list of files. However, when I tried to filter the results based on the metadata createdBy = USERNAME I wasn't able to do it, any guidance would be appreciated.
import { GetObjectOutput } from "aws-sdk/clients/s3";
import * as AWS from 'aws-sdk';
export async function main(): Promise<any> {
const s3 = new AWS.S3();
const params = {
Bucket: 'uni-artifacts',
};
try {
console.log("Listing objects in bucket:", params.Bucket);
const data = await s3.listObjectsV2(params).promise();
if (!data.Contents) {
return {
statusCode: 200,
body: JSON.stringify({ message: "No files found in the bucket" }),
};
}
console.log("Number of objects found:", data.Contents.length);
const files = data.Contents.map(async (obj) => {
try {
const metadata = await getMetadata(s3, obj.Key!, () => {});
return {
Key: obj.Key,
Metadata: metadata,
};
} catch (error) {
console.error("Error getting metadata for", obj.Key, error);
return { Key: obj.Key }; // Example handling, adjust based on needs
}
});
// Wait for all promises to settle (including potential rejections)
const resolvedFiles = await Promise.allSettled(files);
// Filter and handle successful and rejected results
const successfulFiles = resolvedFiles.filter((result) => result.status === "fulfilled")
.map((result) => result); // No need to access value here
const failedFiles = resolvedFiles.filter((result) => result.status === "rejected")
.map((result) => {
// Handle potential undefined data.Contents
if (data?.Contents) {
const failedFile = data.Contents.find(obj => obj.Key === (result as PromiseRejectedResult).reason?.Key); // Type cast to access reason on rejected result
return failedFile ? { Key: failedFile.Key, Error: (result as PromiseRejectedResult).reason?.message } : undefined;
} else {
// Handle case where data.Contents is undefined (e.g., error during listObjectsV2)
return { Error: "Error listing objects in bucket" }; // Example error message, adjust as needed
}
});
const responseBody = {
statusCode: 200,
body: JSON.stringify({
successfulFiles,
failedFiles: failedFiles.filter(file => file), // Filter out undefined entries in failedFiles
}),
};
return responseBody;
} catch (error) {
console.error("Error retrieving files:", error);
return {
statusCode: 500,
body: JSON.stringify({ message: "Error retrieving files" }),
};
}
}
function getMetadata(s3: AWS.S3, key: string, callback: (error: Error | undefined, metadata?: GetObjectOutput["Metadata"]) => void) {
const params = {
Bucket: 'uni-artifacts', // Replace with your bucket name
Key: key,
};
s3.headObject(params, (err, data) => {
if (err) {
console.error("Error getting metadata for", key, err);
return callback(err);
}
console.log("Got metadata for:", key);
callback(undefined, data.Metadata);
});
}
I tried to create an arraylist to store the filtered results. I think my logic is wrong.
import { APIGatewayProxyEvent, APIGatewayProxyResult } from "aws-lambda";
import * as AWS from 'aws-sdk';
interface S3ObjectMetadata {
createdBy?: string; // Optional createdBy property
creationDate?: string; // Optional creationDate property
}
export async function main(event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> {
const s3 = new AWS.S3();
// Parse the username parameter from the request URL
const username = event.queryStringParameters?.username;
console.log("Retrieved Username: " + username);
const params = {
Bucket: 'uni-artifacts',
};
try {
console.log("Listing objects in bucket:", params.Bucket);
const data = await s3.listObjectsV2(params).promise();
if (!data.Contents) {
return {
statusCode: 200,
body: JSON.stringify({ message: "No files found in the bucket" }),
};
}
console.log("Number of objects found:", data.Contents.length);
const files = data.Contents.map(async (obj) => {
try {
const metadata = await getMetadata(s3, obj.Key!);
return {
Key: obj.Key,
Metadata: metadata,
};
} catch (error) {
console.error("Error getting metadata for", obj.Key, error);
return { Key: obj.Key };
}
});
const resolvedFiles = await Promise.allSettled(files);
const successfulFiles = resolvedFiles.filter((result) => result.status === "fulfilled")
.map((result) => {
// Check if the result is fulfilled and has a value
if (result.status === "fulfilled" && result.value) {
const { Key, Metadata } = result.value; // Destructure if value exists
//extract createdBy from metadata
const createdBy = Metadata && typeof Metadata === 'object' && 'createdby' in Metadata ? Metadata.createdby : undefined;
return {
Key,
Metadata: {
createdBy,
}
};
} else {
// Handle unsuccessful results or cases where value is missing
console.error("Unexpected result:", result);
return { Key: undefined, Metadata: {} }; // empty object
}
});
// Filter files based on createdBy metadata matching the username
const filteredFiles = username ?
successfulFiles.filter(file => {
const createdBy = file.Metadata?.createdBy || '';
console.log(`Comparing username '${username}' with createdBy '${createdBy}'`);
const isMatch = createdBy === username;
console.log(`Comparison result: ${isMatch}`);
return isMatch;
}) :
successfulFiles;
console.log("Filtered files:", filteredFiles);
const responseBody = {
statusCode: 200,
body: JSON.stringify({
files: filteredFiles,
}),
};
return responseBody;
} catch (error) {
console.error("Error retrieving files:", error);
return {
statusCode: 500,
body: JSON.stringify({ message: "Error retrieving files" }),
};
}
}
function getMetadata(s3: AWS.S3, key: string): Promise<S3ObjectMetadata | undefined> {
const params = {
Bucket: 'uni-artifacts',
Key: key,
};
return new Promise((resolve, reject) => {
s3.headObject(params, (err, data) => {
if (err) {
console.error("Error getting metadata for", key, err);
reject(err);
} else {
const metadata = data.Metadata;
console.log("Retrieved metadata for " + key, metadata);
resolve(metadata);
}
});
});
}