Add budget & transaction (de)serialization and some cmake improvements
Signed-off-by: William Brawner <me@wbrawner.com>
This commit is contained in:
parent
dc7ba2719f
commit
774b90127a
30 changed files with 890 additions and 271 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,3 +1,5 @@
|
||||||
build/
|
build/
|
||||||
.kdev4/
|
.kdev4/
|
||||||
*.kdev4
|
*.kdev4
|
||||||
|
deps/
|
||||||
|
tags
|
||||||
|
|
10
.vimrc
Normal file
10
.vimrc
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
set colorcolumn=110
|
||||||
|
highlight ColorColumn ctermbg=darkgray
|
||||||
|
augroup project
|
||||||
|
autocmd!
|
||||||
|
autocmd BufRead,BufNewFile *.h,*.c set filetype=c.doxygen
|
||||||
|
augroup END
|
||||||
|
nnoremap <F4> :!mkdir -p build <bar> cmake -B build <bar> make -C build<cr>
|
||||||
|
nnoremap <F4> :!build/PiHelper/pihelper localhost<cr>
|
||||||
|
nnoremap <F9> :!rm -rf build<cr>
|
||||||
|
let &path.="/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include,"
|
1
budget.json
Normal file
1
budget.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"id":"Gbhqm79yC8NereIONtq7W2Mf9MD2y1AU","name":"Monthly Budget","description":"Monthly expenses"}
|
3
include/twigs/api_service.h
Normal file
3
include/twigs/api_service.h
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
#ifndef API_SERVICE_H
|
||||||
|
#define API_SERVICE_H
|
||||||
|
#endif
|
|
@ -1,33 +1,28 @@
|
||||||
#ifndef BUDGET_H
|
#ifndef BUDGET_H
|
||||||
#define BUDGET_H
|
#define BUDGET_H
|
||||||
#include <string>
|
|
||||||
#include "hashable.h"
|
#include "hashable.h"
|
||||||
#include "identifiable.h"
|
#include "identifiable.h"
|
||||||
|
#include "serializable.h"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
class Budget: public Identifiable, public Hashable {
|
class Budget : public Identifiable, public Hashable, public Serializable {
|
||||||
private:
|
private:
|
||||||
string name;
|
string id;
|
||||||
string description;
|
string name;
|
||||||
|
string description;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
string getName() {
|
Budget();
|
||||||
return this->name;
|
Budget(std::string id, std::string name, std::string description);
|
||||||
}
|
string getId() override;
|
||||||
|
string getName();
|
||||||
void setName(string name) {
|
void setName(string name);
|
||||||
this->name = name;
|
string getDescription();
|
||||||
}
|
void setDescription(string description);
|
||||||
|
string hash() override;
|
||||||
string getDescription() {
|
string serialize() override;
|
||||||
return this->description;
|
static Budget deserialize(std::string data);
|
||||||
}
|
|
||||||
|
|
||||||
void setDescription(string description) {
|
|
||||||
this->description = description;
|
|
||||||
}
|
|
||||||
|
|
||||||
string hash() override;
|
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,64 +1,38 @@
|
||||||
#ifndef CATEGORY_H
|
|
||||||
#define CATEGORY_H
|
#define CATEGORY_H
|
||||||
#include <string>
|
#ifndef CATEGORY_H
|
||||||
#include "hashable.h"
|
#include "hashable.h"
|
||||||
#include "identifiable.h"
|
#include "identifiable.h"
|
||||||
|
#include "serializable.h"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
class Category: public Identifiable, public Hashable {
|
class Category : public Identifiable, public Hashable, public Serializable {
|
||||||
private:
|
private:
|
||||||
std::string id;
|
std::string id;
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string description;
|
std::string description;
|
||||||
long amount;
|
unsigned int amount;
|
||||||
bool expense;
|
bool expense;
|
||||||
bool archived;
|
bool archived;
|
||||||
std::string budgetId;
|
std::string budgetId;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
std::string getId() {
|
Category();
|
||||||
return this->id;
|
Category(std::string id, std::string name, std::string budgetId,
|
||||||
}
|
std::string description = "", unsigned int amount = 0,
|
||||||
|
bool expense = true, bool archived = false);
|
||||||
std::string getName() {
|
std::string getId() override;
|
||||||
return this->name;
|
std::string getName();
|
||||||
}
|
void setName(std::string name);
|
||||||
|
std::string getDescription();
|
||||||
void setName(std::string name) {
|
void setDescription(std::string description);
|
||||||
this->name = name;
|
unsigned int getAmount();
|
||||||
}
|
void setAmount(unsigned int amount);
|
||||||
|
bool isExpense();
|
||||||
std::string getDescription() {
|
void setExpense(bool expense);
|
||||||
return this->description;
|
bool isArchived();
|
||||||
}
|
void setArchived(bool archived);
|
||||||
|
std::string hash() override;
|
||||||
void setDescription(std::string description) {
|
string serialize() override;
|
||||||
this->description = description;
|
static Categpry deserialize(std::string data);
|
||||||
}
|
|
||||||
|
|
||||||
long getAmount() {
|
|
||||||
return this->amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setAmount(long amount) {
|
|
||||||
this->amount = amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isExpense() {
|
|
||||||
return this->expense;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setExpense(bool expense) {
|
|
||||||
this->expense = expense;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isArchived() {
|
|
||||||
return this->archived;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setArchived(bool archived) {
|
|
||||||
this->archived = archived;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string hash() override;
|
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
4
include/twigs/db_service.h
Normal file
4
include/twigs/db_service.h
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
#ifndef DB_SERVICE_H
|
||||||
|
#define DB_SERVICE_H
|
||||||
|
|
||||||
|
#endif
|
|
@ -3,15 +3,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
class Identifiable {
|
class Identifiable {
|
||||||
private:
|
|
||||||
std::string id;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
std::string getId() {
|
virtual std::string getId() = 0;
|
||||||
return this->id;
|
|
||||||
}
|
|
||||||
void setId(std::string id) {
|
|
||||||
this->id = id;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -13,6 +13,7 @@ enum Permission {
|
||||||
|
|
||||||
class UserPermission: public Identifiable {
|
class UserPermission: public Identifiable {
|
||||||
private:
|
private:
|
||||||
|
std::string id;
|
||||||
std::string userId;
|
std::string userId;
|
||||||
std::string budgetId;
|
std::string budgetId;
|
||||||
Permission permission;
|
Permission permission;
|
||||||
|
@ -23,6 +24,8 @@ class UserPermission: public Identifiable {
|
||||||
this->budgetId = budgetId;
|
this->budgetId = budgetId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string getId() override;
|
||||||
|
|
||||||
std::string getUserId() {
|
std::string getUserId() {
|
||||||
return this->userId;
|
return this->userId;
|
||||||
}
|
}
|
||||||
|
|
9
include/twigs/serializable.h
Normal file
9
include/twigs/serializable.h
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
#ifndef SERIALIZABLE_H
|
||||||
|
#define SERIALIZABLE_H
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class Serializable {
|
||||||
|
public:
|
||||||
|
virtual std::string serialize() = 0;
|
||||||
|
};
|
||||||
|
#endif
|
|
@ -1,113 +1,51 @@
|
||||||
#ifndef TRANSACTION_H
|
#ifndef TRANSACTION_H
|
||||||
#define TRANSACTION_H
|
#define TRANSACTION_H
|
||||||
#include <string>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include "hashable.h"
|
#include "hashable.h"
|
||||||
#include "identifiable.h"
|
#include "identifiable.h"
|
||||||
|
#include "serializable.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
using namespace std;
|
class Transaction : public Identifiable, public Hashable, public Serializable {
|
||||||
|
private:
|
||||||
|
std::string id;
|
||||||
|
std::string name;
|
||||||
|
std::string description;
|
||||||
|
tm date;
|
||||||
|
unsigned int amount;
|
||||||
|
bool expense;
|
||||||
|
std::string categoryId;
|
||||||
|
std::string budgetId;
|
||||||
|
std::string createdBy;
|
||||||
|
|
||||||
class Transaction: public Identifiable, public Hashable {
|
public:
|
||||||
private:
|
Transaction();
|
||||||
string name;
|
Transaction(std::string id, std::string name, tm date, std::string createdBy,
|
||||||
string description;
|
unsigned int amount = 0, std::string description = "",
|
||||||
tm date;
|
bool expense = true, std::string categoryId = "",
|
||||||
long amount;
|
std::string budgetId = "");
|
||||||
bool expense;
|
std::string getId() override;
|
||||||
bool archived;
|
std::string getName();
|
||||||
string categoryId;
|
void setName(std::string name);
|
||||||
string budgetId;
|
std::string getDescription();
|
||||||
|
void setDescription(std::string description);
|
||||||
public:
|
tm getDate();
|
||||||
Transaction() {
|
void setDate(tm date);
|
||||||
this->name = "";
|
unsigned int getAmount();
|
||||||
this->description = "";
|
void setAmount(unsigned int amount);
|
||||||
time_t now = time(0);
|
bool isExpense();
|
||||||
this->date = *gmtime(&now);
|
void setExpense(bool expense);
|
||||||
this->amount = 0;
|
bool isArchived();
|
||||||
this->expense = true;
|
void setArchived(bool archived);
|
||||||
this->archived = false;
|
std::string getCategoryId();
|
||||||
}
|
void setCategoryId(std::string categoryId);
|
||||||
|
std::string getBudgetId();
|
||||||
string getName() {
|
void setBudgetId(std::string budgetId);
|
||||||
return this->name;
|
std::string getCreatedBy();
|
||||||
}
|
void setCreatedBy(std::string createdBy);
|
||||||
|
std::string hash() override;
|
||||||
void setName(string name) {
|
std::string serialize() override;
|
||||||
this->name = name;
|
static Transaction deserialize(std::string data);
|
||||||
}
|
|
||||||
|
|
||||||
string getDescription() {
|
|
||||||
return this->description;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setDescription(string description) {
|
|
||||||
this->description = description;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the date as a string formatted as YYYY-MM-ddTHH:mm:ssZ
|
|
||||||
*/
|
|
||||||
string getDate() {
|
|
||||||
char date[25];
|
|
||||||
snprintf(
|
|
||||||
date,
|
|
||||||
21,
|
|
||||||
"%04d-%02d-%02dT%02d:%02d:%02dZ",
|
|
||||||
this->date.tm_year,
|
|
||||||
this->date.tm_mon + 1,
|
|
||||||
this->date.tm_mday,
|
|
||||||
this->date.tm_hour,
|
|
||||||
this->date.tm_min,
|
|
||||||
this->date.tm_sec
|
|
||||||
);
|
|
||||||
return date;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the date using YYYY-MM-ddTHH:mm:ssZ format
|
|
||||||
*/
|
|
||||||
void setDate(string date) {
|
|
||||||
//TODO: Handle exceptions
|
|
||||||
int year = stoi(date.substr(0,4));
|
|
||||||
int mon = stoi(date.substr(5, 2));
|
|
||||||
int day = stoi(date.substr(8, 2));
|
|
||||||
int hours = stoi(date.substr(11, 2));
|
|
||||||
int min = stoi(date.substr(14, 2));
|
|
||||||
int sec = stoi(date.substr(17, 2));
|
|
||||||
this->date.tm_year = year;
|
|
||||||
this->date.tm_mon = mon - 1;
|
|
||||||
this->date.tm_mday = day;
|
|
||||||
this->date.tm_hour = hours;
|
|
||||||
this->date.tm_min = min;
|
|
||||||
this->date.tm_sec = sec;
|
|
||||||
}
|
|
||||||
|
|
||||||
long getAmount() {
|
|
||||||
return this->amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setAmount(long amount) {
|
|
||||||
this->amount = amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isExpense() {
|
|
||||||
return this->expense;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setExpense(bool expense) {
|
|
||||||
this->expense = expense;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isArchived() {
|
|
||||||
return this->archived;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setArchived(bool archived) {
|
|
||||||
this->archived = archived;
|
|
||||||
}
|
|
||||||
|
|
||||||
string hash() override;
|
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
13
include/twigs/transaction_repository.h
Normal file
13
include/twigs/transaction_repository.h
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#define TRANSACTION_REPOSITORY_H
|
||||||
|
#ifndef TRANSACTION_REPOSITORY_H
|
||||||
|
#include "transaction.h"
|
||||||
|
|
||||||
|
class TransactionRepository {
|
||||||
|
public:
|
||||||
|
Transaction findById(std::string id);
|
||||||
|
Transaction *findAll(std::string *budgetIds, std::string *categoryIds,
|
||||||
|
std::string *from, std::string *to, int count, int page);
|
||||||
|
Transaction save(Transaction transaction);
|
||||||
|
void remove(Transaction transaction);
|
||||||
|
};
|
||||||
|
#endif
|
|
@ -5,11 +5,13 @@
|
||||||
|
|
||||||
class User: public Identifiable {
|
class User: public Identifiable {
|
||||||
private:
|
private:
|
||||||
|
std::string id;
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string email;
|
std::string email;
|
||||||
std::string avatar;
|
std::string avatar;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
std::string getId() override;
|
||||||
std::string getName();
|
std::string getName();
|
||||||
void setName(std::string name);
|
void setName(std::string name);
|
||||||
std::string getEmail();
|
std::string getEmail();
|
||||||
|
|
12
include/twigs/utils.h
Normal file
12
include/twigs/utils.h
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#ifndef UTILS_H
|
||||||
|
#define UTILS_H
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace util {
|
||||||
|
std::string randomId();
|
||||||
|
std::string hash(std::string data);
|
||||||
|
tm parseDate(std::string str);
|
||||||
|
std::string printDate(tm date);
|
||||||
|
} // namespace util
|
||||||
|
|
||||||
|
#endif
|
198
scripts/build-android
Executable file
198
scripts/build-android
Executable file
|
@ -0,0 +1,198 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Configure the following variables according to your needs
|
||||||
|
|
||||||
|
NDK_VERSION="21.3.6528147"
|
||||||
|
OPENSSL_TAG_VERSION="OpenSSL_1_1_1g "
|
||||||
|
CURL_TAG_VERSION="master "
|
||||||
|
JSONCPP_TAG_VERSION="master"
|
||||||
|
ANDROID_ARCHS="arm arm64 x86 x86_64"
|
||||||
|
NDK=$HOME/Android/Sdk/ndk/$NDK_VERSION
|
||||||
|
MIN_SDK_VERSION=23 # Can't go any lower unfortunately. See the cURL docs for more info.
|
||||||
|
TARGET_SDK_VERSION=29
|
||||||
|
|
||||||
|
# Edit below at your own risk
|
||||||
|
|
||||||
|
get_abi() {
|
||||||
|
case $1 in
|
||||||
|
"arm")
|
||||||
|
echo -n "armeabi-v7a"
|
||||||
|
;;
|
||||||
|
"arm64")
|
||||||
|
echo -n "arm64-v8a"
|
||||||
|
;;
|
||||||
|
"x86")
|
||||||
|
echo -n "x86"
|
||||||
|
;;
|
||||||
|
"x86_64")
|
||||||
|
echo -n "x86_64"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
make_openssl() {
|
||||||
|
(
|
||||||
|
cd deps
|
||||||
|
test -d openssl || git clone --branch $OPENSSL_TAG_VERSION https://github.com/openssl/openssl.git
|
||||||
|
for ARCH in $ANDROID_ARCHS; do
|
||||||
|
(
|
||||||
|
export PREFIX="$BUILD_DIR/android/$ARCH"
|
||||||
|
mkdir -p "$PREFIX"
|
||||||
|
cd openssl
|
||||||
|
git checkout $OPENSSL_TAG_VERSION
|
||||||
|
make clean
|
||||||
|
export ANDROID_NDK_HOME=$NDK
|
||||||
|
export ANDROID_NDK_ROOT=$NDK
|
||||||
|
export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$ANDROID_NDK_HOME/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin:$PATH
|
||||||
|
./Configure \
|
||||||
|
--prefix="$PREFIX" \
|
||||||
|
-D__ANDROID_API__=$TARGET_SDK_VERSION \
|
||||||
|
no-shared \
|
||||||
|
android-$ARCH
|
||||||
|
make install_dev
|
||||||
|
)
|
||||||
|
done
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
make_curl() {
|
||||||
|
(
|
||||||
|
cd deps
|
||||||
|
test -d curl || git clone --branch $CURL_TAG_VERSION https://github.com/curl/curl.git
|
||||||
|
for ARCH in $ANDROID_ARCHS; do
|
||||||
|
(
|
||||||
|
export PREFIX="$BUILD_DIR/android/$ARCH"
|
||||||
|
mkdir -p "$PREFIX"
|
||||||
|
cd curl
|
||||||
|
git checkout $CURL_TAG_VERSION
|
||||||
|
test -f configure || ./buildconf
|
||||||
|
export HOST_TAG=linux-x86_64
|
||||||
|
export TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/$HOST_TAG
|
||||||
|
case $ARCH in
|
||||||
|
"arm")
|
||||||
|
export CURL_ARCH="arm-linux-androideabi"
|
||||||
|
export CLANG="armv7a-linux-androideabi"
|
||||||
|
;;
|
||||||
|
"arm64")
|
||||||
|
export CURL_ARCH="aarch64-linux-android"
|
||||||
|
export CLANG=$CURL_ARCH
|
||||||
|
;;
|
||||||
|
"x86")
|
||||||
|
export CURL_ARCH="i686-linux-android"
|
||||||
|
export CLANG=$CURL_ARCH
|
||||||
|
;;
|
||||||
|
"x86_64")
|
||||||
|
export CURL_ARCH="x86_64-linux-android"
|
||||||
|
export CLANG=$CURL_ARCH
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
export AR=$TOOLCHAIN/bin/$CURL_ARCH-ar
|
||||||
|
export AS=$TOOLCHAIN/bin/$CURL_ARCH-as
|
||||||
|
export CC=$TOOLCHAIN/bin/${CLANG}${MIN_SDK_VERSION}-clang
|
||||||
|
export CXX=$TOOLCHAIN/bin/${CLANG}${MIN_SDK_VERSION}-clang++
|
||||||
|
export LD=$TOOLCHAIN/bin/$CURL_ARCH-ld
|
||||||
|
export RANLIB=$TOOLCHAIN/bin/$CURL_ARCH-ranlib
|
||||||
|
export STRIP=$TOOLCHAIN/bin/$CURL_ARCH-strip
|
||||||
|
make clean
|
||||||
|
./configure --prefix="$PREFIX" \
|
||||||
|
--host $CURL_ARCH \
|
||||||
|
--with-pic \
|
||||||
|
--disable-shared \
|
||||||
|
--with-ssl="$PREFIX"
|
||||||
|
make install
|
||||||
|
)
|
||||||
|
done
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
make_jsoncpp() {
|
||||||
|
(
|
||||||
|
cd deps
|
||||||
|
test -d jsoncpp || git clone --branch $JSONCPP_TAG_VERSION https://github.com/open-source-parsers/jsoncpp
|
||||||
|
for ARCH in $ANDROID_ARCHS; do
|
||||||
|
(
|
||||||
|
export PREFIX="$BUILD_DIR/android/$ARCH"
|
||||||
|
mkdir -p "$PREFIX"
|
||||||
|
cd jsoncpp
|
||||||
|
git checkout $JSONCPP_TAG_VERSION
|
||||||
|
test -d $ARCH && rm -rf $ARCH
|
||||||
|
mkdir $ARCH
|
||||||
|
pushd $ARCH
|
||||||
|
export ABI="$(get_abi $ARCH)"
|
||||||
|
#TODO: CMake is deprecated, this should use meson/ninja
|
||||||
|
cmake \
|
||||||
|
-DCMAKE_TOOLCHAIN_FILE=$NDK/build/cmake/android.toolchain.cmake \
|
||||||
|
-DANDROID_ABI=$ABI \
|
||||||
|
-DANDROID_NATIVE_API_LEVEL=$MIN_SDK_VERSION \
|
||||||
|
-DCMAKE_INSTALL_PREFIX=$PREFIX \
|
||||||
|
-DBUILD_OBJECT_LIBS=OFF \
|
||||||
|
-DBUILD_SHARED_LIBS=OFF \
|
||||||
|
-DJSONCPP_WITH_TESTS=OFF \
|
||||||
|
-DBUILD_STATIC_LIBS=ON \
|
||||||
|
..
|
||||||
|
make
|
||||||
|
make install
|
||||||
|
)
|
||||||
|
done
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
make_twigs() {
|
||||||
|
(
|
||||||
|
for ARCH in $ANDROID_ARCHS; do
|
||||||
|
(
|
||||||
|
export PREFIX="$BUILD_DIR/android/$ARCH"
|
||||||
|
export ABI="$(get_abi $ARCH)"
|
||||||
|
mkdir -p "$PREFIX"
|
||||||
|
cd "$BUILD_DIR"
|
||||||
|
test -d $ARCH && rm -rf $ARCH
|
||||||
|
mkdir $ARCH
|
||||||
|
pushd $ARCH
|
||||||
|
cmake \
|
||||||
|
-DCMAKE_FIND_ROOT_PATH=$PREFIX \
|
||||||
|
-DCMAKE_INCLUDE_PATH=$PREFIX/include \
|
||||||
|
-DCMAKE_PREFIX_PATH=$PREFIX \
|
||||||
|
-DCMAKE_TOOLCHAIN_FILE=$NDK/build/cmake/android.toolchain.cmake \
|
||||||
|
-DANDROID_ABI=$ABI \
|
||||||
|
-DANDROID_NATIVE_API_LEVEL=$MIN_SDK_VERSION \
|
||||||
|
-DCMAKE_INSTALL_PREFIX=$PREFIX \
|
||||||
|
-DBUILD_SHARED_LIBS=OFF \
|
||||||
|
-DBUILD_TESTING=OFF \
|
||||||
|
-DBUILD_STATIC_LIBS=ON \
|
||||||
|
../..
|
||||||
|
make
|
||||||
|
make install
|
||||||
|
)
|
||||||
|
done
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
package() {
|
||||||
|
for ARCH in $ANDROID_ARCHS; do
|
||||||
|
(
|
||||||
|
export ABI="$(get_abi $ARCH)"
|
||||||
|
cd $BUILD_DIR/android
|
||||||
|
cp -r $ARCH/include .
|
||||||
|
mv $ARCH/lib/*.a $ARCH/lib/*.la $ARCH/
|
||||||
|
rm -rf $ARCH/{bin,lib,include,share}
|
||||||
|
if [ "$ARCH" != "$ABI" ]; then
|
||||||
|
mv $ARCH $ABI
|
||||||
|
fi
|
||||||
|
)
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
(
|
||||||
|
if [ "$(dirname $0)" == "." ]; then
|
||||||
|
cd ..
|
||||||
|
fi
|
||||||
|
test -d deps || mkdir deps
|
||||||
|
export BUILD_DIR="$PWD/build"
|
||||||
|
test -d "$BUILD_DIR" || mkdir -p "$BUILD_DIR"
|
||||||
|
make_openssl
|
||||||
|
make_curl
|
||||||
|
make_jsoncpp
|
||||||
|
make_twigs
|
||||||
|
package
|
||||||
|
)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
add_subdirectory(libtwigs)
|
add_subdirectory(libtwigs)
|
||||||
|
|
||||||
option(TWIGS_TESTS "Build tests for Twigs" ON)
|
option(BUILD_TESTING "Build tests for Twigs" ON)
|
||||||
|
|
||||||
if (TWIGS_TESTS)
|
if (BUILD_TESTING)
|
||||||
add_subdirectory(test)
|
add_subdirectory(test)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -25,28 +25,41 @@ set(PUBLIC_HEADERS
|
||||||
${TWIGS_INCLUDE_DIR}/twigs/hashable.h
|
${TWIGS_INCLUDE_DIR}/twigs/hashable.h
|
||||||
${TWIGS_INCLUDE_DIR}/twigs/identifiable.h
|
${TWIGS_INCLUDE_DIR}/twigs/identifiable.h
|
||||||
${TWIGS_INCLUDE_DIR}/twigs/permission.h
|
${TWIGS_INCLUDE_DIR}/twigs/permission.h
|
||||||
|
${TWIGS_INCLUDE_DIR}/twigs/serializable.h
|
||||||
${TWIGS_INCLUDE_DIR}/twigs/transaction.h
|
${TWIGS_INCLUDE_DIR}/twigs/transaction.h
|
||||||
${TWIGS_INCLUDE_DIR}/twigs/user.h
|
${TWIGS_INCLUDE_DIR}/twigs/user.h
|
||||||
|
${TWIGS_INCLUDE_DIR}/twigs/utils.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(TWIGS_SOURCES
|
set(TWIGS_SOURCES
|
||||||
budget.cpp
|
budget.cpp
|
||||||
#category.cpp
|
#category.cpp
|
||||||
transaction.cpp
|
transaction.cpp
|
||||||
|
utils.cpp
|
||||||
#twigs.cpp
|
#twigs.cpp
|
||||||
#user.cpp
|
#user.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
#if (NOT TARGET CURL)
|
if (NOT TARGET CURL)
|
||||||
#find_library(
|
find_library(
|
||||||
#CURL
|
CURL
|
||||||
#NAMES curl libcurl
|
NAMES curl libcurl
|
||||||
#PATHS /usr/local/lib /usr/local/lib64 /usr/lib /usr/lib64 /lib
|
PATHS /usr/local/lib /usr/local/lib64 /usr/lib /usr/lib64 /lib
|
||||||
#)
|
)
|
||||||
#if (NOT CURL)
|
if (NOT CURL)
|
||||||
#message(SEND_ERROR "Did not find curl")
|
message(SEND_ERROR "Did not find curl")
|
||||||
#endif()
|
endif()
|
||||||
#endif()
|
endif()
|
||||||
|
if (NOT TARGET JSONCPP)
|
||||||
|
find_library(
|
||||||
|
JSONCPP
|
||||||
|
NAMES jsoncpp libjsoncpp
|
||||||
|
PATHS /usr/local/lib /usr/local/lib64 /usr/lib /usr/lib64 /lib
|
||||||
|
)
|
||||||
|
if (NOT JSONCPP)
|
||||||
|
message(SEND_ERROR "Did not find jsoncpp")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
#if (NOT TARGET JSONC)
|
#if (NOT TARGET JSONC)
|
||||||
#find_library(
|
#find_library(
|
||||||
#JSONC
|
#JSONC
|
||||||
|
@ -57,31 +70,28 @@ set(TWIGS_SOURCES
|
||||||
#message(SEND_ERROR "Did not find json-c")
|
#message(SEND_ERROR "Did not find json-c")
|
||||||
#endif()
|
#endif()
|
||||||
#endif()
|
#endif()
|
||||||
#if (NOT TARGET CRYPTO)
|
if (NOT TARGET CRYPTO)
|
||||||
#find_library(
|
find_library(
|
||||||
#CRYPTO
|
CRYPTO
|
||||||
#NAMES crypto libcrypto
|
NAMES crypto libcrypto
|
||||||
#PATHS /usr/local/lib /usr/local/lib64 /usr/lib /usr/lib64 /lib
|
PATHS /usr/local/lib /usr/local/lib64 /usr/lib /usr/lib64 /lib
|
||||||
#)
|
)
|
||||||
#if (NOT CRYPTO)
|
if (NOT CRYPTO)
|
||||||
#message(SEND_ERROR "Did not find OpenSSL")
|
message(SEND_ERROR "Did not find OpenSSL")
|
||||||
#endif()
|
endif()
|
||||||
#endif()
|
endif()
|
||||||
#if (NOT TARGET OPENSSL)
|
if (NOT TARGET OPENSSL)
|
||||||
#find_library(
|
find_library(
|
||||||
#OPENSSL
|
OPENSSL
|
||||||
#NAMES ssl libssl
|
NAMES ssl libssl
|
||||||
#PATHS /usr/local/lib /usr/local/lib64 /usr/lib /usr/lib64 /lib
|
PATHS /usr/local/lib /usr/local/lib64 /usr/lib /usr/lib64 /lib
|
||||||
#)
|
)
|
||||||
#if (NOT OPENSSL)
|
if (NOT OPENSSL)
|
||||||
#message(SEND_ERROR "Did not find OpenSSL")
|
message(SEND_ERROR "Did not find OpenSSL")
|
||||||
#endif()
|
endif()
|
||||||
#endif()
|
endif()
|
||||||
|
|
||||||
option(TWIGS_STATIC "Build Twigs as a static library" ON)
|
if (BUILD_SHARED_LIBS)
|
||||||
option(TWIGS_SHARED "Build Twigs as a shared library" OFF)
|
|
||||||
|
|
||||||
if (TWIGS_STATIC)
|
|
||||||
add_library(libtwigs SHARED
|
add_library(libtwigs SHARED
|
||||||
${TWIGS_SOURCES}
|
${TWIGS_SOURCES}
|
||||||
${PUBLIC_HEADERS}
|
${PUBLIC_HEADERS}
|
||||||
|
@ -93,12 +103,20 @@ else()
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_include_directories(libtwigs PRIVATE
|
target_include_directories(libtwigs PUBLIC
|
||||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/${TWIGS_INCLUDE_DIR}>
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/${TWIGS_INCLUDE_DIR}>
|
||||||
$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include/twigs>
|
$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include/twigs>
|
||||||
|
SYSTEM ${CMAKE_INCLUDE_PATH}
|
||||||
|
SYSTEM /usr/local/include
|
||||||
)
|
)
|
||||||
|
|
||||||
|
target_link_libraries(libtwigs ${JSONCPP}
|
||||||
|
${CURL}
|
||||||
|
${CRYPTO}
|
||||||
|
${OPENSSL}
|
||||||
|
)
|
||||||
|
|
||||||
set_target_properties(libtwigs PROPERTIES
|
set_target_properties(libtwigs PROPERTIES
|
||||||
OUTPUT_NAME "twigs"
|
OUTPUT_NAME "twigs"
|
||||||
VERSION ${PROJECT_VERSION}
|
VERSION ${PROJECT_VERSION}
|
||||||
|
@ -106,12 +124,8 @@ set_target_properties(libtwigs PROPERTIES
|
||||||
)
|
)
|
||||||
|
|
||||||
install(TARGETS libtwigs
|
install(TARGETS libtwigs
|
||||||
|
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
OBJECTS DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
#target_link_libraries(twigs ${CURL}
|
|
||||||
#${JSONC}
|
|
||||||
#${CRYPTO}
|
|
||||||
#${OPENSSL}
|
|
||||||
#)
|
|
||||||
|
|
|
@ -1,6 +1,51 @@
|
||||||
#include <twigs/budget.h>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <twigs/budget.h>
|
||||||
|
#include <twigs/utils.h>
|
||||||
|
#include <json/json.h>
|
||||||
|
|
||||||
std::string Budget::hash() {
|
Budget::Budget() : Budget(util::randomId(), "", "") {}
|
||||||
return "";
|
|
||||||
|
Budget::Budget(std::string id, std::string name, std::string description)
|
||||||
|
: id(id), name(name), description(description) {}
|
||||||
|
|
||||||
|
std::string Budget::getId() { return this->id; }
|
||||||
|
|
||||||
|
string Budget::getName() { return this->name; }
|
||||||
|
|
||||||
|
void Budget::setName(string name) { this->name = name; }
|
||||||
|
|
||||||
|
string Budget::getDescription() { return this->description; }
|
||||||
|
|
||||||
|
void Budget::setDescription(string description) {
|
||||||
|
this->description = description;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Budget::serialize() {
|
||||||
|
Json::Value budget;
|
||||||
|
budget["id"] = this->getId();
|
||||||
|
budget["name"] = this->getName();
|
||||||
|
budget["description"] = this->getDescription();
|
||||||
|
Json::StreamWriterBuilder builder;
|
||||||
|
builder["indentation"] = "";
|
||||||
|
return Json::writeString(builder, budget);
|
||||||
|
}
|
||||||
|
|
||||||
|
Budget Budget::deserialize(std::string data) {
|
||||||
|
Json::CharReaderBuilder builder;
|
||||||
|
Json::Value root;
|
||||||
|
if (!builder.newCharReader()->parse(&data[0], &data[0] + data.size(), &root,
|
||||||
|
NULL)) {
|
||||||
|
// TODO: Log errors
|
||||||
|
// std::cout << "Failed to create budget from JSON" << "\n";
|
||||||
|
throw 5;
|
||||||
|
}
|
||||||
|
std::string id = root.get("id", "").asString();
|
||||||
|
if (id == "") {
|
||||||
|
throw 5;
|
||||||
|
}
|
||||||
|
std::string name = root.get("name", "").asString();
|
||||||
|
std::string description = root.get("description", "").asString();
|
||||||
|
return Budget(id, name, description);
|
||||||
|
}
|
||||||
|
|
||||||
|
string Budget::hash() { return util::hash(serialize()); }
|
||||||
|
|
76
src/libtwigs/category.cpp
Normal file
76
src/libtwigs/category.cpp
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
#include <twigs/category.h>
|
||||||
|
#include <twigs/utils.h>
|
||||||
|
|
||||||
|
Category::Category() : Category(util::randomId(), "", "") {}
|
||||||
|
|
||||||
|
Category::Category(std::string id, std::string name, std::string budgetId,
|
||||||
|
std::string description, unsigned int amount, bool expense,
|
||||||
|
bool archived)
|
||||||
|
: id(id), name(name), budgetId(budgetId), description(description),
|
||||||
|
amount(amount), expense(expense), archived(archived) {}
|
||||||
|
|
||||||
|
std::string Category::getId() { return this->id; }
|
||||||
|
|
||||||
|
std::string Category::getName() { return this->name; }
|
||||||
|
|
||||||
|
void Category::setName(std::string name) { this->name = name; }
|
||||||
|
|
||||||
|
std::string Category::getDescription() { return this->description; }
|
||||||
|
|
||||||
|
void Category::setDescription(std::string description) {
|
||||||
|
this->description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
long Category::getAmount() { return this->amount; }
|
||||||
|
|
||||||
|
void Category::setAmount(long amount) { this->amount = amount; }
|
||||||
|
|
||||||
|
bool Category::isExpense() { return this->expense; }
|
||||||
|
|
||||||
|
void Category::setExpense(bool expense) { this->expense = expense; }
|
||||||
|
|
||||||
|
bool Category::isArchived() { return this->archived; }
|
||||||
|
|
||||||
|
void Category::setArchived(bool archived) { this->archived = archived; }
|
||||||
|
|
||||||
|
std::string Category::getBudgetId() { return this->budgetId; }
|
||||||
|
|
||||||
|
void Category::setBudgetId(std::string budgetId) { this->budgetId = budgetId; }
|
||||||
|
|
||||||
|
std::string Category::serialize() {
|
||||||
|
Json::Value category;
|
||||||
|
category["id"] = this->getId();
|
||||||
|
category["name"] = this->getName();
|
||||||
|
category["description"] = this->getDescription();
|
||||||
|
category["amount"] = this->getAmount();
|
||||||
|
category["expense"] = this->isExpense();
|
||||||
|
category["archived"] = this->isArchived();
|
||||||
|
category["budgetId"] = this->getBudgetId();
|
||||||
|
Json::StreamWriterBuilder builder;
|
||||||
|
builder["indentation"] = "";
|
||||||
|
return Json::writeString(builder, transaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
Category Category::deserialize(std::string data) {
|
||||||
|
Json::CharReaderBuilder builder;
|
||||||
|
Json::Value root;
|
||||||
|
if (!builder.newCharReader()->parse(&data[0], &data[0] + data.size(), &root,
|
||||||
|
NULL)) {
|
||||||
|
// TODO: Log errors
|
||||||
|
// std::cout << "Failed to create transaction from JSON" << "\n";
|
||||||
|
throw 5;
|
||||||
|
}
|
||||||
|
std::string id = root.get("id", "").asString();
|
||||||
|
if (id == "") {
|
||||||
|
throw 5;
|
||||||
|
}
|
||||||
|
std::string name = root.get("name", "").asString();
|
||||||
|
std::string description = root.get("description", "").asString();
|
||||||
|
long amount = root.get("amount", 0).asUInt();
|
||||||
|
bool expense = root.get("expense", true).asBool();
|
||||||
|
bool archived = root.get("archived", true).asBool();
|
||||||
|
std::string budgetId = root.get("budgetId", "").asString();
|
||||||
|
return Category(id, name, description);
|
||||||
|
}
|
||||||
|
|
||||||
|
string Category::hash() { return util::hash(serialize()); }
|
|
@ -1,8 +1,105 @@
|
||||||
|
#include <json/json.h>
|
||||||
|
#include <twigs/serializable.h>
|
||||||
#include <twigs/transaction.h>
|
#include <twigs/transaction.h>
|
||||||
|
#include <twigs/utils.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
string Transaction::hash() {
|
tm *now() {
|
||||||
throw 5;
|
time_t now = time(NULL);
|
||||||
// return "";
|
return gmtime(&now);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Transaction::Transaction() : Transaction(util::randomId(), "", *now(), "") {}
|
||||||
|
|
||||||
|
Transaction::Transaction(std::string id, std::string name, tm date,
|
||||||
|
std::string createdBy, unsigned int amount,
|
||||||
|
std::string description, bool expense,
|
||||||
|
std::string categoryId, std::string budgetId)
|
||||||
|
: id(id), name(name), description(description), date(date), amount(amount),
|
||||||
|
expense(expense), categoryId(categoryId), budgetId(budgetId),
|
||||||
|
createdBy(createdBy) {}
|
||||||
|
|
||||||
|
string Transaction::getId() { return this->id; }
|
||||||
|
|
||||||
|
string Transaction::getName() { return this->name; }
|
||||||
|
|
||||||
|
void Transaction::setName(std::string name) { this->name = name; }
|
||||||
|
|
||||||
|
string Transaction::getDescription() { return this->description; }
|
||||||
|
|
||||||
|
void Transaction::setDescription(string description) {
|
||||||
|
this->description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
tm Transaction::getDate() { return this->date; }
|
||||||
|
|
||||||
|
void Transaction::setDate(tm date) { this->date = date; }
|
||||||
|
|
||||||
|
unsigned int Transaction::getAmount() { return this->amount; }
|
||||||
|
|
||||||
|
void Transaction::setAmount(unsigned int amount) { this->amount = amount; }
|
||||||
|
|
||||||
|
bool Transaction::isExpense() { return this->expense; }
|
||||||
|
|
||||||
|
void Transaction::setExpense(bool expense) { this->expense = expense; }
|
||||||
|
|
||||||
|
string Transaction::getCategoryId() { return this->categoryId; }
|
||||||
|
|
||||||
|
void Transaction::setCategoryId(string categoryId) {
|
||||||
|
this->categoryId = categoryId;
|
||||||
|
}
|
||||||
|
|
||||||
|
string Transaction::getBudgetId() { return this->budgetId; }
|
||||||
|
|
||||||
|
void Transaction::setBudgetId(string budgetId) { this->budgetId = budgetId; }
|
||||||
|
|
||||||
|
string Transaction::getCreatedBy() { return this->createdBy; }
|
||||||
|
|
||||||
|
void Transaction::setCreatedBy(string createdBy) {
|
||||||
|
this->createdBy = createdBy;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Transaction::serialize() {
|
||||||
|
Json::Value transaction;
|
||||||
|
transaction["id"] = this->getId();
|
||||||
|
transaction["title"] = this->getName();
|
||||||
|
transaction["description"] = this->getDescription();
|
||||||
|
transaction["date"] = util::printDate(this->getDate());
|
||||||
|
transaction["amount"] = this->getAmount();
|
||||||
|
transaction["expense"] = this->isExpense();
|
||||||
|
transaction["budgetId"] = this->getBudgetId();
|
||||||
|
transaction["categoryId"] = this->getCategoryId();
|
||||||
|
transaction["createdBy"] = this->getCreatedBy();
|
||||||
|
Json::StreamWriterBuilder builder;
|
||||||
|
builder["indentation"] = "";
|
||||||
|
return Json::writeString(builder, transaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
Transaction Transaction::deserialize(std::string data) {
|
||||||
|
Json::CharReaderBuilder builder;
|
||||||
|
Json::Value root;
|
||||||
|
if (!builder.newCharReader()->parse(&data[0], &data[0] + data.size(), &root,
|
||||||
|
NULL)) {
|
||||||
|
// TODO: Log errors
|
||||||
|
// std::cout << "Failed to create transaction from JSON" << "\n";
|
||||||
|
throw 5;
|
||||||
|
}
|
||||||
|
std::string id = root.get("id", "").asString();
|
||||||
|
if (id == "") {
|
||||||
|
throw 5;
|
||||||
|
}
|
||||||
|
std::string name = root.get("title", "").asString();
|
||||||
|
std::string description = root.get("description", "").asString();
|
||||||
|
std::string dateStr = root.get("date", "").asString();
|
||||||
|
tm date = util::parseDate(dateStr);
|
||||||
|
long amount = root.get("amount", 0).asUInt();
|
||||||
|
bool expense = root.get("expense", true).asBool();
|
||||||
|
std::string budgetId = root.get("budgetId", "").asString();
|
||||||
|
std::string categoryId = root.get("categoryId", "").asString();
|
||||||
|
std::string createdBy = root.get("createdBy", "").asString();
|
||||||
|
return Transaction(id, name, date, createdBy, amount, description, expense,
|
||||||
|
categoryId, budgetId);
|
||||||
|
}
|
||||||
|
|
||||||
|
string Transaction::hash() { return util::hash(serialize()); }
|
||||||
|
|
61
src/libtwigs/utils.cpp
Normal file
61
src/libtwigs/utils.cpp
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
#include <random>
|
||||||
|
#include <twigs/utils.h>
|
||||||
|
#include <openssl/sha.h>
|
||||||
|
|
||||||
|
namespace util {
|
||||||
|
std::string randomId() {
|
||||||
|
const std::string CHARACTERS =
|
||||||
|
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||||
|
const int ID_LEN = 32;
|
||||||
|
|
||||||
|
std::random_device rd;
|
||||||
|
std::mt19937 engine(rd());
|
||||||
|
std::uniform_int_distribution<> distribution(0, CHARACTERS.size() - 1);
|
||||||
|
|
||||||
|
std::string random_string;
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < ID_LEN; ++i) {
|
||||||
|
random_string += CHARACTERS[distribution(engine)];
|
||||||
|
}
|
||||||
|
|
||||||
|
return random_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
tm parseDate(std::string str) {
|
||||||
|
static tm date;
|
||||||
|
// TODO: Handle exceptions
|
||||||
|
int year = stoi(str.substr(0, 4));
|
||||||
|
int mon = stoi(str.substr(5, 2));
|
||||||
|
int day = stoi(str.substr(8, 2));
|
||||||
|
int hours = stoi(str.substr(11, 2));
|
||||||
|
int min = stoi(str.substr(14, 2));
|
||||||
|
int sec = stoi(str.substr(17, 2));
|
||||||
|
date.tm_year = year;
|
||||||
|
date.tm_mon = mon - 1;
|
||||||
|
date.tm_mday = day;
|
||||||
|
date.tm_hour = hours;
|
||||||
|
date.tm_min = min;
|
||||||
|
date.tm_sec = sec;
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string printDate(tm date) {
|
||||||
|
static char str[25];
|
||||||
|
snprintf(str, 21, "%04d-%02d-%02dT%02d:%02d:%02dZ", date.tm_year,
|
||||||
|
date.tm_mon + 1, date.tm_mday, date.tm_hour, date.tm_min,
|
||||||
|
date.tm_sec);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string hash(std::string data) {
|
||||||
|
unsigned char bytes[SHA256_DIGEST_LENGTH];
|
||||||
|
SHA256((unsigned char *) &data[0], data.size(), bytes);
|
||||||
|
char hash[(SHA256_DIGEST_LENGTH * 2) + 1];
|
||||||
|
int i;
|
||||||
|
for(i = 0; i < SHA256_DIGEST_LENGTH; i++) {
|
||||||
|
sprintf(hash + (i * 2), "%02x", bytes[i]);
|
||||||
|
}
|
||||||
|
hash[SHA256_DIGEST_LENGTH * 2] = '\0';
|
||||||
|
return std::string(hash, SHA256_DIGEST_LENGTH * 2);
|
||||||
|
}
|
||||||
|
} // namespace util
|
|
@ -6,29 +6,27 @@ set(TEST_SOURCES
|
||||||
budget_test.cpp
|
budget_test.cpp
|
||||||
transaction_test.h
|
transaction_test.h
|
||||||
transaction_test.cpp
|
transaction_test.cpp
|
||||||
|
utils_test.h
|
||||||
|
utils_test.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
find_library(CPPUNIT cppunit)
|
find_library(CPPUNIT cppunit)
|
||||||
|
|
||||||
add_executable(twigs_test
|
add_executable(twigs_test ${TEST_SOURCES})
|
||||||
${TEST_SOURCES}
|
|
||||||
${PUBLIC_HEADERS}
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_libraries(twigs_test libtwigs ${CPPUNIT})
|
target_link_libraries(twigs_test libtwigs ${CPPUNIT})
|
||||||
|
|
||||||
target_include_directories(twigs_test PRIVATE
|
|
||||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
|
||||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/${TWIGS_INCLUDE_DIR}>
|
|
||||||
$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include/twigs>
|
|
||||||
)
|
|
||||||
|
|
||||||
message("CMAKE_INSTALL_INCLUDEDIR: ${CMAKE_INSTALL_INCLUDEDIR}")
|
|
||||||
message("CMAKE_CURRENT_LIST_DIR}/TWIGS_INCLUDE_DIR ${CMAKE_CURRENT_LIST_DIR}/${TWIGS_INCLUDE_DIR}")
|
|
||||||
message("PROJECT_BINARY_DIR/include/twigs: ${PROJECT_BINARY_DIR}/include/twigs}")
|
|
||||||
|
|
||||||
set_target_properties(twigs_test PROPERTIES
|
set_target_properties(twigs_test PROPERTIES
|
||||||
OUTPUT_NAME "twigstest"
|
OUTPUT_NAME "twigstest"
|
||||||
VERSION ${PROJECT_VERSION}
|
VERSION ${PROJECT_VERSION}
|
||||||
SOVERSION ${PROJECT_VERSION_MAJOR}
|
SOVERSION ${PROJECT_VERSION_MAJOR}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_test(NAME twigs_test
|
||||||
|
COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $<TARGET_FILE:twigs_test>
|
||||||
|
)
|
||||||
|
|
||||||
|
add_custom_command(TARGET twigs_test
|
||||||
|
POST_BUILD
|
||||||
|
COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $<TARGET_FILE:twigs_test>
|
||||||
|
)
|
||||||
|
|
|
@ -1,6 +1,41 @@
|
||||||
#include <cppunit/extensions/HelperMacros.h>
|
|
||||||
#include "budget_test.h"
|
#include "budget_test.h"
|
||||||
|
#include <cppunit/extensions/HelperMacros.h>
|
||||||
|
|
||||||
void BudgetTest::parseDate() {
|
void BudgetTest::serialize() {
|
||||||
Budget budget;
|
const std::string json =
|
||||||
|
R"({"description":"Monthly expenses","id":"Gbhqm79yC8NereIONtq7W2Mf9MD2y1AU","name":"Monthly Budget"})";
|
||||||
|
Budget b = Budget("Gbhqm79yC8NereIONtq7W2Mf9MD2y1AU", "Monthly Budget",
|
||||||
|
"Monthly expenses");
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("Budget serialization failed", json,
|
||||||
|
b.serialize());
|
||||||
|
}
|
||||||
|
|
||||||
|
void BudgetTest::deserialize() {
|
||||||
|
const std::string json =
|
||||||
|
R"({"description":"Monthly expenses","id":"Gbhqm79yC8NereIONtq7W2Mf9MD2y1AU","name":"Monthly Budget"})";
|
||||||
|
Budget b = Budget::deserialize(json);
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("Budget id parsing failed",
|
||||||
|
std::string("Gbhqm79yC8NereIONtq7W2Mf9MD2y1AU"),
|
||||||
|
b.getId());
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("Budget name parsing failed",
|
||||||
|
std::string("Monthly Budget"), b.getName());
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("Budget description parsing failed",
|
||||||
|
std::string("Monthly expenses"),
|
||||||
|
b.getDescription());
|
||||||
|
}
|
||||||
|
|
||||||
|
void BudgetTest::defaultIdsDifferent() {
|
||||||
|
Budget b1;
|
||||||
|
Budget b2;
|
||||||
|
CPPUNIT_ASSERT(b1.getId() != b2.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
void BudgetTest::hashBudget() {
|
||||||
|
Budget b = Budget("Gbhqm79yC8NereIONtq7W2Mf9MD2y1AU", "Monthly Budget",
|
||||||
|
"Monthly expenses");
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE(
|
||||||
|
"Budget hash failed",
|
||||||
|
std::string(
|
||||||
|
"12fd354740ec016a4b50e2edd1e586107784fe7b88001d8d6ab7ba9ce38aa1d6"),
|
||||||
|
b.hash());
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,16 @@
|
||||||
|
|
||||||
class BudgetTest: public CppUnit::TestCase {
|
class BudgetTest: public CppUnit::TestCase {
|
||||||
public:
|
public:
|
||||||
void parseDate();
|
void serialize();
|
||||||
|
void deserialize();
|
||||||
|
void defaultIdsDifferent();
|
||||||
|
void hashBudget();
|
||||||
|
|
||||||
CPPUNIT_TEST_SUITE( BudgetTest );
|
CPPUNIT_TEST_SUITE( BudgetTest );
|
||||||
CPPUNIT_TEST( parseDate );
|
CPPUNIT_TEST( serialize );
|
||||||
|
CPPUNIT_TEST( deserialize );
|
||||||
|
CPPUNIT_TEST( defaultIdsDifferent );
|
||||||
|
CPPUNIT_TEST( hashBudget );
|
||||||
CPPUNIT_TEST_SUITE_END();
|
CPPUNIT_TEST_SUITE_END();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
#include <cppunit/TextTestRunner.h>
|
#include <cppunit/TextTestRunner.h>
|
||||||
#include "budget_test.h"
|
#include "budget_test.h"
|
||||||
#include "transaction_test.h"
|
#include "transaction_test.h"
|
||||||
|
#include "utils_test.h"
|
||||||
|
|
||||||
|
CPPUNIT_TEST_SUITE_REGISTRATION (UtilsTest);
|
||||||
|
CPPUNIT_TEST_SUITE_REGISTRATION (BudgetTest);
|
||||||
CPPUNIT_TEST_SUITE_REGISTRATION (TransactionTest);
|
CPPUNIT_TEST_SUITE_REGISTRATION (TransactionTest);
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
BudgetTest budgetTest;
|
BudgetTest budgetTest;
|
||||||
TransactionTest transactionTest;
|
TransactionTest transactionTest;
|
||||||
|
UtilsTest utilsTest;
|
||||||
|
|
||||||
CppUnit::Test *test = CppUnit::TestFactoryRegistry::getRegistry().makeTest();
|
CppUnit::Test *test = CppUnit::TestFactoryRegistry::getRegistry().makeTest();
|
||||||
CppUnit::TextTestRunner runner;
|
CppUnit::TextTestRunner runner;
|
||||||
|
|
|
@ -1,9 +1,84 @@
|
||||||
|
#include "transaction_test.h"
|
||||||
#include <cppunit/TestCase.h>
|
#include <cppunit/TestCase.h>
|
||||||
#include <cppunit/extensions/HelperMacros.h>
|
#include <cppunit/extensions/HelperMacros.h>
|
||||||
#include "transaction_test.h"
|
#include <iostream>
|
||||||
|
#include <time.h>
|
||||||
|
#include <twigs/utils.h>
|
||||||
|
|
||||||
void TransactionTest::parseDate() {
|
void TransactionTest::serialize() {
|
||||||
Transaction transaction;
|
const std::string json =
|
||||||
transaction.setDate("2020-01-31T23:59:59Z");
|
R"({"amount":2108,"budgetId":"Gbhqm79yC8NereIONtq7W2Mf9MD2y1AU","categoryId":"Q4R8QLhSksL2wi7XzmrqhL06qm2aZ2S4","createdBy":"1","date":"2021-01-08T13:06:00Z","description":"Books","expense":true,"id":"2snbSS8flrSH6OrvmSEmqyvaduOi2uc2","title":"Amazon"})";
|
||||||
CPPUNIT_ASSERT_EQUAL_MESSAGE("Transaction date parsing failed", std::string("2020-01-31T23:59:59Z"), transaction.getDate());
|
|
||||||
|
tm date;
|
||||||
|
date.tm_year = 2021;
|
||||||
|
date.tm_mon = 0;
|
||||||
|
date.tm_mday = 8;
|
||||||
|
date.tm_hour = 13;
|
||||||
|
date.tm_min = 6;
|
||||||
|
date.tm_sec = 0;
|
||||||
|
Transaction t =
|
||||||
|
Transaction("2snbSS8flrSH6OrvmSEmqyvaduOi2uc2", "Amazon", date, "1", 2108,
|
||||||
|
"Books", true, "Q4R8QLhSksL2wi7XzmrqhL06qm2aZ2S4",
|
||||||
|
"Gbhqm79yC8NereIONtq7W2Mf9MD2y1AU");
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("Transaction serialization failed", json,
|
||||||
|
t.serialize());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransactionTest::deserialize() {
|
||||||
|
const std::string json =
|
||||||
|
R"({"id":"2snbSS8flrSH6OrvmSEmqyvaduOi2uc2","title":"Amazon","description":"Books","date":"2021-01-08T13:06:00Z","amount":2108,"expense":true,"budgetId":"Gbhqm79yC8NereIONtq7W2Mf9MD2y1AU","categoryId":"Q4R8QLhSksL2wi7XzmrqhL06qm2aZ2S4","createdBy":"1"})";
|
||||||
|
Transaction t = Transaction::deserialize(json);
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("Transaction id parsing failed",
|
||||||
|
std::string("2snbSS8flrSH6OrvmSEmqyvaduOi2uc2"),
|
||||||
|
t.getId());
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("Transaction name parsing failed",
|
||||||
|
std::string("Amazon"), t.getName());
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("Transaction description parsing failed",
|
||||||
|
std::string("Books"), t.getDescription());
|
||||||
|
tm date = t.getDate();
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("Transaction date year parsing failed", 2021,
|
||||||
|
date.tm_year);
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("Transaction date month parsing failed", 0,
|
||||||
|
date.tm_mon);
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("Transaction date day parsing failed", 8,
|
||||||
|
date.tm_mday);
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("Transaction date hour parsing failed", 13,
|
||||||
|
date.tm_hour);
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("Transaction date min parsing failed", 6,
|
||||||
|
date.tm_min);
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("Transaction date sec parsing failed", 0,
|
||||||
|
date.tm_sec);
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("Transaction amount parsing failed", 2108U,
|
||||||
|
t.getAmount());
|
||||||
|
CPPUNIT_ASSERT_MESSAGE("Transaction expense parsing failed", t.isExpense());
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("Transaction budgetId parsing failed",
|
||||||
|
std::string("Gbhqm79yC8NereIONtq7W2Mf9MD2y1AU"),
|
||||||
|
t.getBudgetId());
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("Transaction category parsing failed",
|
||||||
|
std::string("Q4R8QLhSksL2wi7XzmrqhL06qm2aZ2S4"),
|
||||||
|
t.getCategoryId());
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("Transaction createdBy parsing failed",
|
||||||
|
std::string("1"), t.getCreatedBy());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransactionTest::defaultIdsDifferent() {
|
||||||
|
Transaction t1;
|
||||||
|
Transaction t2;
|
||||||
|
CPPUNIT_ASSERT(t1.getId() != t2.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransactionTest::hashTransaction() {
|
||||||
|
tm date;
|
||||||
|
date.tm_year = 2021;
|
||||||
|
date.tm_mon = 0;
|
||||||
|
date.tm_mday = 8;
|
||||||
|
date.tm_hour = 13;
|
||||||
|
date.tm_min = 6;
|
||||||
|
date.tm_sec = 0;
|
||||||
|
Transaction t =
|
||||||
|
Transaction("2snbSS8flrSH6OrvmSEmqyvaduOi2uc2", "Amazon", date, "1", 2108,
|
||||||
|
"Books", true, "Q4R8QLhSksL2wi7XzmrqhL06qm2aZ2S4",
|
||||||
|
"Gbhqm79yC8NereIONtq7W2Mf9MD2y1AU");
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("Transaction hash failed",
|
||||||
|
std::string("94140eb2923e905360cef1e7aca3d321da39f0608ea17eb944475d81b89a64cb"), t.hash());
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,16 @@
|
||||||
|
|
||||||
class TransactionTest: public CppUnit::TestCase {
|
class TransactionTest: public CppUnit::TestCase {
|
||||||
public:
|
public:
|
||||||
void parseDate();
|
void serialize();
|
||||||
|
void deserialize();
|
||||||
|
void defaultIdsDifferent();
|
||||||
|
void hashTransaction();
|
||||||
|
|
||||||
CPPUNIT_TEST_SUITE( TransactionTest );
|
CPPUNIT_TEST_SUITE( TransactionTest );
|
||||||
CPPUNIT_TEST( parseDate );
|
CPPUNIT_TEST( serialize );
|
||||||
|
CPPUNIT_TEST( deserialize );
|
||||||
|
CPPUNIT_TEST( defaultIdsDifferent );
|
||||||
|
CPPUNIT_TEST( hashTransaction );
|
||||||
CPPUNIT_TEST_SUITE_END();
|
CPPUNIT_TEST_SUITE_END();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
24
src/test/utils_test.cpp
Normal file
24
src/test/utils_test.cpp
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#include "utils_test.h"
|
||||||
|
#include <cppunit/TestCase.h>
|
||||||
|
#include <cppunit/extensions/HelperMacros.h>
|
||||||
|
|
||||||
|
void UtilsTest::parseDateTest() {}
|
||||||
|
|
||||||
|
void UtilsTest::printDateTest() {
|
||||||
|
tm date;
|
||||||
|
date.tm_year = 2020;
|
||||||
|
date.tm_mon = 0;
|
||||||
|
date.tm_mday = 31;
|
||||||
|
date.tm_hour = 23;
|
||||||
|
date.tm_min = 59;
|
||||||
|
date.tm_sec = 59;
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("Date printing failed",
|
||||||
|
std::string("2020-01-31T23:59:59Z"),
|
||||||
|
util::printDate(date));
|
||||||
|
}
|
||||||
|
|
||||||
|
void UtilsTest::hashTest() {
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("Hashing failed",
|
||||||
|
std::string("9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"),
|
||||||
|
util::hash("test"));
|
||||||
|
}
|
21
src/test/utils_test.h
Normal file
21
src/test/utils_test.h
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#ifndef UTILS_TEST_H_INCLUDED
|
||||||
|
#define UTILS_TEST_H_INCLUDED
|
||||||
|
|
||||||
|
#include <cppunit/TestCase.h>
|
||||||
|
#include <cppunit/extensions/HelperMacros.h>
|
||||||
|
#include <twigs/utils.h>
|
||||||
|
|
||||||
|
class UtilsTest: public CppUnit::TestCase {
|
||||||
|
public:
|
||||||
|
void parseDateTest();
|
||||||
|
void printDateTest();
|
||||||
|
void hashTest();
|
||||||
|
|
||||||
|
CPPUNIT_TEST_SUITE( UtilsTest );
|
||||||
|
CPPUNIT_TEST( parseDateTest );
|
||||||
|
CPPUNIT_TEST( printDateTest );
|
||||||
|
CPPUNIT_TEST( hashTest );
|
||||||
|
CPPUNIT_TEST_SUITE_END();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // UTILS_TEST_H_INCLUDED
|
1
transaction.json
Normal file
1
transaction.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"amount":2108,"budgetId":"Gbhqm79yC8NereIONtq7W2Mf9MD2y1AU","categoryId":"Q4R8QLhSksL2wi7XzmrqhL06qm2aZ2S4","createdBy":"1","date":"2021-01-08T13:06:00Z","description":"Books","expense":true,"id":"2snbSS8flrSH6OrvmSEmqyvaduOi2uc2","title":"Amazon"}
|
Loading…
Reference in a new issue