drogon/drogon_ctl/create_model.h

404 lines
11 KiB
C
Raw Normal View History

/**
*
* create_model.h
* An Tao
*
* Copyright 2018, An Tao. All rights reserved.
2018-12-07 15:50:18 +08:00
* https://github.com/an-tao/drogon
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
2018-11-16 13:26:14 +08:00
* Drogon
*
*/
#pragma once
#include <drogon/config.h>
#include <drogon/DrTemplateBase.h>
#include <json/json.h>
2018-11-11 12:02:48 +08:00
#include <drogon/orm/DbClient.h>
using namespace drogon::orm;
#include <drogon/DrObject.h>
#include "CommandHandler.h"
#include <string>
2019-10-26 23:44:31 +08:00
#include <algorithm>
using namespace drogon;
namespace drogon_ctl
{
struct ColumnInfo
{
2019-11-21 11:27:47 +08:00
std::string colName_;
std::string colValName_;
std::string colTypeName_;
std::string colType_;
std::string colDatabaseType_;
std::string dbType_;
ssize_t colLength_{0};
size_t index_{0};
bool isAutoVal_{false};
bool isPrimaryKey_{false};
bool notNull_{false};
bool hasDefaultVal_{false};
};
2019-10-26 23:44:31 +08:00
inline std::string nameTransform(const std::string &origName, bool isType)
{
auto str = origName;
std::transform(str.begin(), str.end(), str.begin(), tolower);
std::string::size_type startPos = 0;
std::string::size_type pos;
std::string ret;
do
{
pos = str.find("_", startPos);
if (pos == std::string::npos)
{
pos = str.find(".", startPos);
}
if (pos != std::string::npos)
ret += str.substr(startPos, pos - startPos);
else
{
ret += str.substr(startPos);
break;
}
while (str[pos] == '_' || str[pos] == '.')
2019-11-21 11:27:47 +08:00
++pos;
2019-10-26 23:44:31 +08:00
if (str[pos] >= 'a' && str[pos] <= 'z')
str[pos] += ('A' - 'a');
startPos = pos;
} while (1);
if (isType && ret[0] >= 'a' && ret[0] <= 'z')
ret[0] += ('A' - 'a');
return ret;
}
class PivotTable
{
public:
PivotTable() = default;
PivotTable(const Json::Value &json)
2020-04-25 02:12:44 +08:00
: tableName_(json.get("table_name", "").asString())
2019-10-26 23:44:31 +08:00
{
2019-11-21 11:27:47 +08:00
if (tableName_.empty())
2019-10-26 23:44:31 +08:00
{
throw std::runtime_error("table_name can't be empty");
}
2019-11-21 11:27:47 +08:00
originalKey_ = json.get("original_key", "").asString();
if (originalKey_.empty())
2019-10-26 23:44:31 +08:00
{
throw std::runtime_error("original_key can't be empty");
}
2019-11-21 11:27:47 +08:00
targetKey_ = json.get("target_key", "").asString();
if (targetKey_.empty())
2019-10-26 23:44:31 +08:00
{
throw std::runtime_error("target_key can't be empty");
}
}
PivotTable reverse() const
{
PivotTable pivot;
2019-11-21 11:27:47 +08:00
pivot.tableName_ = tableName_;
pivot.originalKey_ = targetKey_;
pivot.targetKey_ = originalKey_;
2019-10-26 23:44:31 +08:00
return pivot;
}
const std::string &tableName() const
{
2019-11-21 11:27:47 +08:00
return tableName_;
2019-10-26 23:44:31 +08:00
}
const std::string &originalKey() const
{
2019-11-21 11:27:47 +08:00
return originalKey_;
2019-10-26 23:44:31 +08:00
}
const std::string &targetKey() const
{
2019-11-21 11:27:47 +08:00
return targetKey_;
2019-10-26 23:44:31 +08:00
}
private:
2019-11-21 11:27:47 +08:00
std::string tableName_;
std::string originalKey_;
std::string targetKey_;
2019-10-26 23:44:31 +08:00
};
2021-03-12 10:41:20 +08:00
class ConvertMethod
{
public:
ConvertMethod(const Json::Value &convert)
{
tableName_ = convert.get("table", "*").asString();
colName_ = convert.get("column", "*").asString();
auto method = convert["method"];
if (method.isNull())
{
throw std::runtime_error("method - object is missing.");
} // endif
if (!method.isObject())
{
throw std::runtime_error("method is not an object.");
} // endif
methodBeforeDbWrite_ = method.get("before_db_write", "").asString();
methodAfterDbRead_ = method.get("after_db_read", "").asString();
auto includeFiles = convert["includes"];
if (includeFiles.isNull())
{
return;
} // endif
if (!includeFiles.isArray())
{
throw std::runtime_error("includes must be an array");
} // endif
for (auto &i : includeFiles)
{
includeFiles_.push_back(i.asString());
} // for
}
ConvertMethod() = default;
bool shouldConvert(const std::string &tableName,
const std::string &colName) const;
const std::string &tableName() const
{
return tableName_;
}
const std::string &colName() const
{
return colName_;
}
const std::string &methodBeforeDbWrite() const
{
return methodBeforeDbWrite_;
}
const std::string &methodAfterDbRead() const
{
return methodAfterDbRead_;
}
const std::vector<std::string> &includeFiles() const
{
return includeFiles_;
}
private:
std::string tableName_{"*"};
std::string colName_{"*"};
std::string methodBeforeDbWrite_;
std::string methodAfterDbRead_;
std::vector<std::string> includeFiles_;
};
2019-10-26 23:44:31 +08:00
class Relationship
{
public:
enum class Type
{
HasOne,
HasMany,
ManyToMany
};
Relationship(const Json::Value &relationship)
{
auto type = relationship.get("type", "has one").asString();
if (type == "has one")
{
2019-11-21 11:27:47 +08:00
type_ = Relationship::Type::HasOne;
2019-10-26 23:44:31 +08:00
}
else if (type == "has many")
{
2019-11-21 11:27:47 +08:00
type_ = Relationship::Type::HasMany;
2019-10-26 23:44:31 +08:00
}
else if (type == "many to many")
{
2019-11-21 11:27:47 +08:00
type_ = Relationship::Type::ManyToMany;
2019-10-26 23:44:31 +08:00
}
else
{
char message[128];
2020-03-01 11:50:47 +08:00
snprintf(message,
sizeof(message),
"Invalid relationship type: %s",
type.data());
2019-10-26 23:44:31 +08:00
throw std::runtime_error(message);
}
2019-11-21 11:27:47 +08:00
originalTableName_ =
2019-10-26 23:44:31 +08:00
relationship.get("original_table_name", "").asString();
2019-11-21 11:27:47 +08:00
if (originalTableName_.empty())
2019-10-26 23:44:31 +08:00
{
throw std::runtime_error("original_table_name can't be empty");
}
2019-11-21 11:27:47 +08:00
originalKey_ = relationship.get("original_key", "").asString();
if (originalKey_.empty())
2019-10-26 23:44:31 +08:00
{
throw std::runtime_error("original_key can't be empty");
}
2019-11-21 11:27:47 +08:00
originalTableAlias_ =
2019-10-26 23:44:31 +08:00
relationship.get("original_table_alias", "").asString();
2019-11-21 11:27:47 +08:00
targetTableName_ = relationship.get("target_table_name", "").asString();
if (targetTableName_.empty())
2019-10-26 23:44:31 +08:00
{
throw std::runtime_error("target_table_name can't be empty");
}
2019-11-21 11:27:47 +08:00
targetKey_ = relationship.get("target_key", "").asString();
if (targetKey_.empty())
2019-10-26 23:44:31 +08:00
{
throw std::runtime_error("target_key can't be empty");
}
2019-11-21 11:27:47 +08:00
targetTableAlias_ =
2019-10-26 23:44:31 +08:00
relationship.get("target_table_alias", "").asString();
2019-11-21 11:27:47 +08:00
enableReverse_ = relationship.get("enable_reverse", false).asBool();
if (type_ == Type::ManyToMany)
2019-10-26 23:44:31 +08:00
{
auto &pivot = relationship["pivot_table"];
if (pivot.isNull())
{
throw std::runtime_error(
"ManyToMany relationship needs a pivot table");
}
2019-11-21 11:27:47 +08:00
pivotTable_ = PivotTable(pivot);
2019-10-26 23:44:31 +08:00
}
}
Relationship() = default;
Relationship reverse() const
{
Relationship r;
2019-11-21 11:27:47 +08:00
if (type_ == Type::HasMany)
2019-10-26 23:44:31 +08:00
{
2019-11-21 11:27:47 +08:00
r.type_ = Type::HasOne;
2019-10-26 23:44:31 +08:00
}
else
{
2019-11-21 11:27:47 +08:00
r.type_ = type_;
2019-10-26 23:44:31 +08:00
}
2019-11-21 11:27:47 +08:00
r.originalTableName_ = targetTableName_;
r.originalTableAlias_ = targetTableAlias_;
r.originalKey_ = targetKey_;
r.targetTableName_ = originalTableName_;
r.targetTableAlias_ = originalTableAlias_;
r.targetKey_ = originalKey_;
r.enableReverse_ = enableReverse_;
r.pivotTable_ = pivotTable_.reverse();
2019-10-26 23:44:31 +08:00
return r;
}
Type type() const
{
2019-11-21 11:27:47 +08:00
return type_;
2019-10-26 23:44:31 +08:00
}
bool enableReverse() const
{
2019-11-21 11:27:47 +08:00
return enableReverse_;
2019-10-26 23:44:31 +08:00
}
const std::string &originalTableName() const
{
2019-11-21 11:27:47 +08:00
return originalTableName_;
2019-10-26 23:44:31 +08:00
}
const std::string &originalTableAlias() const
{
2019-11-21 11:27:47 +08:00
return originalTableAlias_;
2019-10-26 23:44:31 +08:00
}
const std::string &originalKey() const
{
2019-11-21 11:27:47 +08:00
return originalKey_;
2019-10-26 23:44:31 +08:00
}
const std::string &targetTableName() const
{
2019-11-21 11:27:47 +08:00
return targetTableName_;
2019-10-26 23:44:31 +08:00
}
const std::string &targetTableAlias() const
{
2019-11-21 11:27:47 +08:00
return targetTableAlias_;
2019-10-26 23:44:31 +08:00
}
const std::string &targetKey() const
{
2019-11-21 11:27:47 +08:00
return targetKey_;
2019-10-26 23:44:31 +08:00
}
const PivotTable &pivotTable() const
{
2019-11-21 11:27:47 +08:00
return pivotTable_;
2019-10-26 23:44:31 +08:00
}
private:
2019-11-21 11:27:47 +08:00
Type type_{Type::HasOne};
std::string originalTableName_;
std::string originalTableAlias_;
std::string targetTableName_;
std::string targetTableAlias_;
std::string originalKey_;
std::string targetKey_;
bool enableReverse_{false};
PivotTable pivotTable_;
2019-10-26 23:44:31 +08:00
};
class create_model : public DrObject<create_model>, public CommandHandler
{
2019-05-18 20:39:57 +08:00
public:
virtual void handleCommand(std::vector<std::string> &parameters) override;
virtual std::string script() override
{
return "create Model classes files";
}
2019-05-18 20:39:57 +08:00
protected:
void createModel(const std::string &path,
const std::string &singleModelName);
void createModel(const std::string &path,
const Json::Value &config,
const std::string &singleModelName);
#if USE_POSTGRESQL
2021-03-12 10:41:20 +08:00
void createModelClassFromPG(
const std::string &path,
const DbClientPtr &client,
const std::string &tableName,
const std::string &schema,
const Json::Value &restfulApiConfig,
const std::vector<Relationship> &relationships,
const std::vector<ConvertMethod> &convertMethods);
2019-10-26 23:44:31 +08:00
void createModelFromPG(
const std::string &path,
const DbClientPtr &client,
const std::string &schema,
const Json::Value &restfulApiConfig,
2021-03-12 10:41:20 +08:00
std::map<std::string, std::vector<Relationship>> &relationships,
std::map<std::string, std::vector<ConvertMethod>> &convertMethods);
2018-12-01 11:38:05 +08:00
#endif
#if USE_MYSQL
2019-10-26 23:44:31 +08:00
void createModelClassFromMysql(
const std::string &path,
const DbClientPtr &client,
const std::string &tableName,
const Json::Value &restfulApiConfig,
2021-03-12 10:41:20 +08:00
const std::vector<Relationship> &relationships,
const std::vector<ConvertMethod> &convertMethods);
2019-10-26 23:44:31 +08:00
void createModelFromMysql(
const std::string &path,
const DbClientPtr &client,
const Json::Value &restfulApiConfig,
2021-03-12 10:41:20 +08:00
std::map<std::string, std::vector<Relationship>> &relationships,
std::map<std::string, std::vector<ConvertMethod>> &convertMethods);
2018-12-28 18:22:02 +08:00
#endif
#if USE_SQLITE3
2019-10-26 23:44:31 +08:00
void createModelClassFromSqlite3(
const std::string &path,
const DbClientPtr &client,
const std::string &tableName,
const Json::Value &restfulApiConfig,
2021-03-12 10:41:20 +08:00
const std::vector<Relationship> &relationships,
const std::vector<ConvertMethod> &convertMethod);
2019-10-26 23:44:31 +08:00
void createModelFromSqlite3(
const std::string &path,
const DbClientPtr &client,
const Json::Value &restfulApiConfig,
2021-03-12 10:41:20 +08:00
std::map<std::string, std::vector<Relationship>> &relationships,
std::map<std::string, std::vector<ConvertMethod>> &convertMethod);
#endif
void createRestfulAPIController(const DrTemplateData &tableInfo,
const Json::Value &restfulApiConfig);
2019-11-21 11:27:47 +08:00
std::string dbname_;
bool forceOverwrite_{false};
};
2019-05-18 20:39:57 +08:00
} // namespace drogon_ctl