diff --git a/models/record.js b/models/record.js new file mode 100644 index 0000000..71421d4 --- /dev/null +++ b/models/record.js @@ -0,0 +1,32 @@ +import mongoose from "mongoose"; +const Schema = mongoose.Schema; + +const RecordSchema = new Schema( + { + date: Number, + quotes: [ + { + baseCurrency: String, + rates: [{ currency: String, rate: Number }], + }, + ], + institutions: [ + { + name: String, + country: String, + assets: [ + { + currency: String, + amount: Number, + isEarning: Boolean, + description: String, + }, + ], + }, + ], + }, + { timestamps: true } +); + +module.exports = + mongoose.models.Record || mongoose.model("Record", RecordSchema); diff --git a/package.json b/package.json index 4063ea8..171338a 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "scripts": { "all": "npm-run-all -p storybook dev", - "dev": "next dev", + "dev": "NODE_OPTIONS='--inspect' next dev", "build": "next build", "start": "next start", "lint": "next lint", diff --git a/serverActions/appendRecord.js b/serverActions/appendRecord.js index d34e274..159725d 100644 --- a/serverActions/appendRecord.js +++ b/serverActions/appendRecord.js @@ -1,8 +1,9 @@ "use server"; -import mongoose from "mongoose"; import Ajv from "ajv"; -const dbUri = process.env.MONGO_URI; +import connect from "utils/mongooseConnect"; +import Record from "models/record"; +import { getQuotes } from "./getQuotes"; const baseCurrencies = ["usd", "rub", "amd", "brl"]; const ajv = new Ajv(); @@ -21,7 +22,6 @@ const recordSchema = { country: { type: "string", maxLength: 2, - minLength: 2, }, assets: { type: "array", @@ -31,7 +31,6 @@ const recordSchema = { properties: { currency: { type: "string", - maxLength: 3, minLength: 3, }, amount: { @@ -59,10 +58,13 @@ const recordSchema = { export async function appendRecord(formData) { const isFormDataValid = ajv.validate(recordSchema, formData); + console.log("isFormDataValid", isFormDataValid); if (!isFormDataValid) { throw new Error(`Provided data has wrong structure: ${ajv.errorsText()}`); } // TODO add error handling to ui + // TODO add success handling to UI + // TODO prevent adding multiple records per month. Ask to override const recordInstitutions = formData.institutions .filter((institution) => !institution.isDeleted) @@ -76,75 +78,17 @@ export async function appendRecord(formData) { ) ); - const record = { + const record = new Record({ date: Date.now(), quotes: await getQuotes({ baseCurrencies, recordCurrencies }), institutions: recordInstitutions, - }; + }); - console.log("record:", record); - - // push to db - // if no database? - - // await mongoose.connect(uri, { - // useNewUrlParser: true, - // serverSelectionTimeoutMS: 5000, - // socketTimeoutMS: 45000, - // family: 4, // Use IPv4, skip trying IPv6 - // }); - // console.log("models:", mongoose.models); - - // console.log("Mongoose connected to MongoDB Atlas!"); - // const kittySchema = new mongoose.Schema({ - // name: String, - // }); - // const Cat = mongoose.models.Cat || mongoose.model("Cat", kittySchema); - // console.log("Cat modeled"); - - // const fluffy = new Cat({ name: "fluffy" }); - // fluffy.save; -} - -async function getQuotes({ baseCurrencies, recordCurrencies }) { - const fetchUrls = recordCurrencies.map( - (currency) => - `https://cdn.jsdelivr.net/npm/@fawazahmed0/currency-api@latest/v1/currencies/${currency}.json` - ); - - async function fetchQuotes(fetchUrls) { - try { - const promises = fetchUrls.map((url) => fetch(url)); - const responses = await Promise.all(promises); - const data = await Promise.all( - responses.map((response) => response.json()) - ); - return data; - } catch (error) { - console.error(error); - } + try { + await connect(); + await record.save(); + console.log("Document saved to db"); + } catch (error) { + throw new Error("Error saving document:", error); } - - // TODO reformat quotes because currencies can be 4 or more symbols. and `usdtcny` will be unable to parse - /* [ - {recordCurrency: target currencies} - {usd:[rub:90, amd:400...]} - {rub:[rub:1, usd: 0.01...]} - ] */ - const quotes = (await fetchQuotes(fetchUrls)) - .map((quote) => { - const { date, ...rest } = quote; - const [baseCurrency] = Object.keys(rest); - const quotes = Object.entries(rest[baseCurrency]); - debugger; - const targetQuotes = quotes - .filter(([key]) => baseCurrencies.includes(key) && key != baseCurrency) - .map(([currency, value]) => ({ - [`${baseCurrency}${currency}`]: value, - })); - return targetQuotes; - }) - .flat(); - - return quotes; } diff --git a/serverActions/getQuotes.js b/serverActions/getQuotes.js new file mode 100644 index 0000000..581d10d --- /dev/null +++ b/serverActions/getQuotes.js @@ -0,0 +1,38 @@ +"use server"; + +export async function getQuotes({ baseCurrencies, recordCurrencies }) { + const fetchUrls = recordCurrencies.map( + (currency) => + `https://cdn.jsdelivr.net/npm/@fawazahmed0/currency-api@latest/v1/currencies/${currency}.json` + ); + + async function fetchQuotes(fetchUrls) { + try { + const promises = fetchUrls.map((url) => fetch(url)); + const responses = await Promise.all(promises); + const data = await Promise.all( + responses.map((response) => response.json()) + ); + return data; + } catch (error) { + console.error(error); + } + } + + const quotes = (await fetchQuotes(fetchUrls)) + .map((quote) => { + const { date, ...rest } = quote; + const [baseCurrency] = Object.keys(rest); + const quotes = Object.entries(rest[baseCurrency]); + const targetQuotes = quotes + .filter(([key]) => baseCurrencies.includes(key)) + .map(([currency, value]) => ({ + currency: currency, + rate: value, + })); + return { baseCurrency: baseCurrency, rates: [...targetQuotes] }; + }) + .flat(); + + return quotes; +} diff --git a/utils/mongooseConnect.js b/utils/mongooseConnect.js new file mode 100644 index 0000000..1d80703 --- /dev/null +++ b/utils/mongooseConnect.js @@ -0,0 +1,12 @@ +import mongoose from "mongoose"; + +const connect = async () => { + try { + await mongoose.connect(process.env.MONGO_URI); + console.log("Database connected!"); + } catch (error) { + throw new Error("Connection failed!"); + } +}; + +export default connect;