вторник, 18 июля 2017 г.

JSON Server на MS SQL 2016


В MS SQL 2016 появилась возможность работать с json.
И появилась идея сделать аналог популярного json-server (JSON-Server as a Fake REST API in Frontend Development), только с иными возможностями, не как Fake JSON-Server, а как полнофункциональный, описывающий динамически все модели/сущности, логику и данные для отображения.

Идея проста, процедура DB получает json с описанием что делать, и возвращает результат в виде json.
Простой REST сервер имеет всего один POST метод вызывающий процедуру DB. Это не очень правильно все делать одним post методом, но нет пока время все красиво сделать :)

Все действия (создание/правка/удаление моделей/данных/логики) это post запросы.
Все модели, логика - являются данными, никакого хардкода :)

Сделал все это дело за три выходных дня, и код далек от совершенства :)
Данный вариант простой и можно дорабатывать до бесконечности.

Пример как с этим работать:
Создадим таблицы Product, Order, History.
Product - список продуктов
Order - список заказанных продуктов
History - при добавлении заказа данные о нем помещаются в таблицу History

Создание таблицы Product:
{
"Operation": "ChangeModel",
"Action": "ins",
"Data":
{
"TableName": "Product",
"Columns":
[
{"Column": "Name", "Value": "nvarchar(50)"},
{"Column": "Price", "Value": "int"}
]
}
}

Создание таблицы Order:
{
"Operation": "ChangeModel",
"Action": "ins",
"Data":
{
"TableName": "Order",
"Columns":
[
{"Column": "Product", "Value": "int"},
{"Column": "Qty", "Value": "int"},
{"Column": "Price", "Value": "int"}
]
}
}

Создание таблицы History:
{
"Operation": "ChangeModel",
"Action": "ins",
"Data":
{
"TableName": "History",
"Columns":
[
   {"Column": "Product", "Value": "int"},
{"Column": "Qty", "Value": "int"},
{"Column": "Summa", "Value": "int"}
]
}
}

Добавление логики (при добавлении заказа данные о нем помещаются в таблицу History):
{
"Operation": "ChangeLogic",
"Action": "ins",
"Name": "Add to History",
"ActionTable": "Order",
"TriggerTable": "History",
"ActionType": "ins",
"TriggerActionType": "ins",
"TriggerLinkExp": "",
"Data":
{
"Columns":
[
{"Column": "Product", "Exp": "[Order].[OrderId]"},
{"Column": "Qty", "Exp": "[Order].[Qty]"},
{"Column": "Summa", "Exp": "SUM([Order].[Qty] * [Order].[Price])"}
]
}
}

Все, структура/таблицы/модели и логика созданы.

Добавляет данные в таблицу Product:
{
"Operation": "ChangeData",
"Action": "ins",
"Data":
{
"TableName": "Product",
"Columns":
[
{"Column": "Name", "Value": "Product 1"},
{"Column": "Price", "Value": "5"}
]
}
}

Добавляет данные в таблицу Order:
{
"Operation": "ChangeData",
"Action": "ins",
"Data":
{
"TableName": "Order",
"Columns":
[
{"Column": "Product", "Value": "1"},
{"Column": "Qty", "Value": "10"},
{"Column": "Price", "Value": "5"}
]
}
}

Все, данные добавили.

Теперь создаем модель получения результата (все Order c Qty > 0):
{
"Operation": "GetDataModel",
"Name": "Get Orders",
"Tables":
[
{
"TableName": "Order",
"Columns": [ "Product", "Qty", "Price" ],
"Criteria": "Qty > 0"
}
]
}

Теперь создаем модель получения результата (все History):
{
"Operation": "GetDataModel",
"Name": "Get History",
"Tables":
[
{
"TableName": "History",
"Columns": [ "Product", "Qty", "Summa" ]
}
]
}

Получить результат (Order):
{
"Operation": "GetData",
"Name": "Get Orders",
"Tables":
[
{
"TableName": "Order"
}
]
}

Результат: 
[{"Order":[{"Product":1,"Qty":10,"Price":5}]}]

Получить результат (History):
{
"Operation": "GetData",
"Name": "Get History",
"Tables":
[
{
"TableName": "History"
}
]
}

Результат: 
[{"History":[{"Product":1,"Qty":10,"Summa":50}]}]


Старт сервера:

1) Выполнить скрипт (только для MSSQL 2016): script.sql
2) Прописать в server.js CONNECTION (DB по умолчанию 'Test')
3) npm install
4) node server.js

http://localhost:8081/api/data

Server:

package.json:
{
  "dependencies":
  {
    "body-parser": "^1.17.2",
    "edge": "^7.10.1",
    "edge-sql": "^0.1.2",
    "edge-sql-plus": "0.0.10",
    "express": "^4.15.3"
  }
}

server.js:
var CONNECTION = "Server=localhost\\SQL2016;Database=Test;Integrated Security=True";
var PORT = 8081;

var express = require('express');
var bodyParser = require("body-parser");
var app = express();
var edge = require('edge');

app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

var server = app.listen(PORT,
    function() {
        var port = server.address().port
        console.log("RESTful API server started on port", port);
    });

app.post('/api/data', function (req, res) {
    executeQuery(req, res);
});

var executeQuery = function (req, res) {
    var str = JSON.stringify(req.body).toString();
    str = str.replace(/\\r|\\n|\\t/g, '').replace(/\\"/g, '"').substring(2);

    var execProc = edge.func('sql-plus', {
        source: 'exec RunAction',
        connectionString: CONNECTION
    });

    execProc({json: str},
        function(err, result) {
            if (err) {
                console.log(err);
                res.send(err);
            }
            console.log(result);
            res.send(result);
        });
}

script.sql:

IF NOT EXISTS (SELECT * FROM sys.databases WHERE name = N'Test')
CREATE DATABASE [Test]
GO

USE Test
GO

-------------------------------------------------------------------------------------------------------------------------------------------------
-- DROP ALL
-------------------------------------------------------------------------------------------------------------------------------------------------
DROP PROCEDURE IF EXISTS [dbo].[RunAction]
DROP PROCEDURE IF EXISTS [dbo].[ExecTrigger]
DROP PROCEDURE IF EXISTS [dbo].[UpdateModel]
DROP PROCEDURE IF EXISTS [dbo].[UpdateData]
DROP PROCEDURE IF EXISTS [dbo].[GetData]
DROP PROCEDURE IF EXISTS [dbo].[ExecAction]

DROP FUNCTION IF EXISTS  [dbo].[fGetExp]

DROP TYPE IF EXISTS [dbo].[TriggerColumn]
DROP TYPE IF EXISTS [dbo].[TableColumn]
DROP TYPE IF EXISTS [dbo].[TableData]

DROP TABLE IF EXISTS [dbo].[t_trigger_item]
DROP TABLE IF EXISTS [dbo].[t_trigger]
DROP TABLE IF EXISTS [dbo].[t_getdata]
GO

-------------------------------------------------------------------------------------------------------------------------------------------------
-- CREATE TYPES
-------------------------------------------------------------------------------------------------------------------------------------------------
CREATE TYPE [dbo].[TriggerColumn] AS TABLE(
trigger_table varchar(50) not null,
trigger_column varchar(50) not null,
trigger_exp nvarchar(max) not null
)
GO
CREATE TYPE [dbo].[TableColumn] AS TABLE(
column_name varchar(50) not null,
column_val nvarchar(max) null,
column_type int not null
)
GO
CREATE TYPE [dbo].[TableData] AS TABLE(
table_name varchar(50) not null,
table_columns nvarchar(max) not null,
criteria nvarchar(4000) null
)
GO

-------------------------------------------------------------------------------------------------------------------------------------------------
-- CREATE TABLES
-------------------------------------------------------------------------------------------------------------------------------------------------
CREATE TABLE [dbo].[t_trigger]
(
trigger_id int NOT NULL IDENTITY (1, 1),
name varchar(50) NOT NULL,
action_table varchar(50) NOT NULL,
trigger_table varchar(50) NOT NULL,
action_type nchar(3) NOT NULL,
trigger_action_type nchar(3) NOT NULL,
trigger_link_exp nvarchar(4000) NULL,
)  ON [PRIMARY]
GO
ALTER TABLE dbo.t_trigger ADD CONSTRAINT PK_trigger_id PRIMARY KEY CLUSTERED (trigger_id)
GO

CREATE TABLE [dbo].[t_trigger_item]
(
trigger_item_id int NOT NULL IDENTITY (1, 1),
trigger_id int NOT NULL,
trigger_column varchar(50) NOT NULL,
trigger_exp nvarchar(4000) NOT NULL
)  ON [PRIMARY]
GO
ALTER TABLE dbo.t_trigger_item ADD CONSTRAINT PK_trigger_item_id PRIMARY KEY CLUSTERED (trigger_item_id)
GO
ALTER TABLE dbo.t_trigger_item ADD CONSTRAINT FK_trigger_item_trigger FOREIGN KEY (trigger_id) REFERENCES dbo.t_trigger (trigger_id)
GO

CREATE TABLE [dbo].[t_getdata]
(
getdata_id int NOT NULL IDENTITY (1, 1),
getdata_name nvarchar(50) NOT NULL,
getdata_json nvarchar(max) NOT NULL
)  ON [PRIMARY]
GO
ALTER TABLE dbo.t_getdata ADD CONSTRAINT PK_t_getdata_id PRIMARY KEY CLUSTERED (getdata_id)
GO

-------------------------------------------------------------------------------------------------------------------------------------------------
-- CREATE FUNCTIONS
-------------------------------------------------------------------------------------------------------------------------------------------------
CREATE FUNCTION [dbo].[fGetExp](@str nvarchar(4000))
RETURNS @table_exp TABLE (exp_key nvarchar(50), exp_val nvarchar(500), exp_str nvarchar(4000), exp_tables nvarchar(500), exp_sql nvarchar(4000), exp_sql_total nvarchar(max))
AS
BEGIN
declare @exp int = 1
declare @begin int
declare @mid int
declare @end int

declare @exp_val_temp VARCHAR(500)

while(CHARINDEX('SUM(', @str) > 0 OR CHARINDEX('COUNT(', @str) > 0)
begin
set @begin = (select min(i) from( select CHARINDEX('SUM(', @str) i union select CHARINDEX('COUNT(', @str) as i) a where i > 0)
set @end = CHARINDEX(')', substring(@str, @begin, LEN(@str) - @begin + 1))

insert into @table_exp (exp_key, exp_val)
values (cast(@exp as varchar) + 'EXP', substring(@str, @begin, @end))

set @str = stuff(@str, @begin, @end, cast(@exp as varchar) + 'EXP')
set @exp = @exp + 1
end

while(CHARINDEX('[', @str) > 0)
begin
set @begin = CHARINDEX('[', @str)
set @end = CHARINDEX(']', REPLACE(@str, '].[', '...')) - @begin + 1

insert into @table_exp (exp_key, exp_val)
values (cast(@exp as varchar) + 'EXP', substring(@str, @begin, @end))

set @str = stuff(@str, @begin, @end, cast(@exp as varchar) + 'EXP')
set @exp = @exp + 1
end

DECLARE @exp_key VARCHAR(50), @exp_val VARCHAR(500), @exp_tables VARCHAR(500)
DECLARE cursor1 CURSOR LOCAL FOR
SELECT exp_key, exp_val
FROM @table_exp
OPEN cursor1
FETCH NEXT FROM cursor1 INTO @exp_key, @exp_val
WHILE @@FETCH_STATUS=0
BEGIN
while(CHARINDEX('[', @exp_val) > 0)
begin
set @begin = CHARINDEX('[', @exp_val)
set @end = CHARINDEX('].[', @exp_val) - @begin + 1 

if not exists(select 1 from @table_exp where exp_key = @exp_key and exp_tables != substring(@exp_val, @begin, @end))
update @table_exp set exp_tables = substring(@exp_val, @begin, @end)
where exp_key = @exp_key

set @exp_val = stuff(@exp_val, @begin, @end + 2, '')
end

FETCH NEXT FROM cursor1 INTO @exp_key, @exp_val
END
CLOSE cursor1
DEALLOCATE cursor1

declare @exp_sql_total nvarchar(max) = @str;

DECLARE cursor2 CURSOR LOCAL FOR
SELECT exp_key, exp_val, exp_tables
FROM @table_exp
OPEN cursor2
FETCH NEXT FROM cursor2 INTO @exp_key, @exp_val, @exp_tables
WHILE @@FETCH_STATUS=0
BEGIN
declare @sqt_ nvarchar(4000) = '(SELECT ' + @exp_val + ' FROM ' + @exp_tables + ' WHERE ' + REPLACE(@exp_tables,']','Id]') + ' = @ID)'
update @table_exp set exp_sql = @sqt_
where exp_key = @exp_key

set @exp_sql_total = replace(@exp_sql_total, @exp_key, @sqt_)

FETCH NEXT FROM cursor2 INTO @exp_key, @exp_val, @exp_tables
END
CLOSE cursor2
DEALLOCATE cursor2

update @table_exp set exp_str = @str, exp_sql_total = @exp_sql_total
return;
END
GO

-------------------------------------------------------------------------------------------------------------------------------------------------
-- CREATE PROCEDURES
-------------------------------------------------------------------------------------------------------------------------------------------------
CREATE PROCEDURE [dbo].[ExecTrigger]
@id_action int,
@trigger_link_exp nvarchar(4000),
@trigger_type char(3),
@trigger_table varchar(50),
@TriggerColumns TriggerColumn READONLY
as
begin
SET NOCOUNT ON

declare @exp_sql_total nvarchar(max)
declare @sql nvarchar(max)
declare @cols nvarchar(max) = ''
declare @vals nvarchar(max) = ''

if @trigger_type = 'ins'
begin
set @sql = 'INSERT INTO ' + @trigger_table +'(#cols) VALUES (#vals)'
end
if @trigger_type = 'upd'
begin
set @sql = 'UPDATE ' + @trigger_table +' SET #cols'
end
if @trigger_type = 'del'
begin
set @sql = 'DELETE FROM ' + @trigger_table
end

DECLARE @trigger_column varchar(50), @trigger_exp nvarchar(4000)
DECLARE cursor1 CURSOR LOCAL FOR
SELECT trigger_column, trigger_exp
FROM @TriggerColumns
OPEN cursor1
FETCH NEXT FROM cursor1 INTO @trigger_column, @trigger_exp
WHILE @@FETCH_STATUS=0
BEGIN
select top 1 @exp_sql_total = exp_sql_total
from dbo.fGetExp(@trigger_exp)

if @trigger_type = 'ins'
begin
if len(@cols) > 0 set @cols = @cols + ','
set @cols = @cols + @trigger_column

if len(@vals) > 0 set @vals = @vals + ','
set @vals = @vals + @exp_sql_total
end
if @trigger_type = 'upd'
begin
if len(@cols) > 0 set @cols = @cols + ','
set @cols = @cols + @trigger_column + ' = (' + @exp_sql_total + ')'
end

FETCH NEXT FROM cursor1 INTO @trigger_column, @trigger_exp
END
CLOSE cursor1
DEALLOCATE cursor1


if @trigger_type = 'ins'
begin
set @sql = REPLACE(@sql, '#cols', @cols)
set @sql = REPLACE(@sql, '#vals', @vals)

EXEC sp_executeSql @sql, N'@ID INT', @id_action
end
if @trigger_type = 'upd'
begin
set @sql = REPLACE(@sql, '#cols', @cols)

IF LEN(ISNULL(@trigger_link_exp,'')) > 0
begin
set @sql = @sql + ' WHERE ' + @trigger_link_exp
end

EXEC sp_executeSql @sql, N'@ID INT', @id_action
end
if @trigger_type = 'del'
begin
IF LEN(ISNULL(@trigger_link_exp,'')) > 0
begin
set @sql = @sql + ' WHERE ' + @trigger_link_exp
end

EXEC sp_executeSql @sql, N'@ID INT', @id_action
end
end
GO

CREATE PROCEDURE [dbo].[UpdateModel]
@Action char(3),
@TableName varchar(50),
@TableColumn TableColumn readonly
as
begin
SET NOCOUNT ON

declare @sql nvarchar(max) = ''

if (@Action = 'ins')
begin
set @sql = @sql + '[' + @TableName + 'Id] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,'

select @sql = @sql + column_name + ' ' + column_val + ','
from @TableColumn

set @sql = 'DROP TABLE IF EXISTS [dbo].[' + @TableName + ']; CREATE TABLE [dbo].[' + @TableName + '](' + @sql + ');'
EXEC sp_executeSql @sql
end
if (@Action = 'upd')
begin
select @sql = @sql + 'ALTER TABLE ' + @TableName + case when COL_LENGTH(@TableName, column_name) IS NULL then ' ADD ' else ' ALTER COLUMN ' end + column_name + ' ' + column_val + '; '
from @TableColumn

EXEC sp_executeSql @sql
end
if (@Action = 'del')
begin
set @sql = 'DROP TABLE IF EXISTS [dbo].[' + @TableName + '];'
EXEC sp_executeSql @sql
end
end
GO

CREATE PROCEDURE [dbo].[UpdateData]
@Action char(3),
@TableName varchar(50),
@TableColumn TableColumn readonly,
@CurrentId int output
as
begin
SET NOCOUNT ON

declare @sql nvarchar(max) = ''
declare @sqlCol nvarchar(max) = ''
declare @sqlVal nvarchar(max) = ''

if (@Action = 'ins')
begin
select @sqlCol = @sqlCol + column_name + ',', @sqlVal = @sqlVal + '''' + column_val + ''','
from @TableColumn

set @sqlCol = SUBSTRING(@sqlCol, 1, LEN(@sqlCol)-1)
set @sqlVal = SUBSTRING(@sqlVal, 1, LEN(@sqlVal)-1)

set @sql = 'INSERT INTO [dbo].[' + @TableName + '](' + @sqlCol + ') VALUES(' + @sqlVal + '); SET @CurrentId = scope_identity();'
EXEC sp_executeSql @sql, N'@CurrentId INT OUTPUT', @CurrentId OUTPUT
end
if (@Action = 'upd')
begin
select @sqlCol = @sqlCol + column_name + ' = ''' + column_val + ''','
from @TableColumn

set @sqlCol = SUBSTRING(@sqlCol, 1, LEN(@sqlCol)-1)

set @sql = 'UPDATE [dbo].[' + @TableName + '] SET ' + @sqlCol + ' WHERE _Id = ' + CAST(ISNULL(@CurrentId, 0) as varchar) + ';'
EXEC sp_executeSql @sql
end
if (@Action = 'del')
begin
set @sql = 'DELETE FROM [dbo].[' + @TableName + '] WHERE _Id = ' + CAST(ISNULL(@CurrentId, 0) as varchar) + ';'
EXEC sp_executeSql @sql
end
end
GO

CREATE PROCEDURE [dbo].[GetData]
@TableData TableData readonly,
@TableDataCriteria TableData readonly
as
begin
SET NOCOUNT ON

declare @sql nvarchar(max) = ''
declare @sqlTable varchar(50) = ''
declare @sqlCol nvarchar(max) = ''
declare @criteria nvarchar(4000)
declare @criteriaAdd nvarchar(4000)
declare @json nvarchar(max) = ''

declare @res nvarchar(max)

select @sqlTable = table_name, @sqlCol = table_columns, @criteria = criteria
from @TableData

DECLARE cursor1 CURSOR LOCAL FOR
select table_name, table_columns, criteria
from @TableData
OPEN cursor1
FETCH NEXT FROM cursor1 INTO @sqlTable, @sqlCol, @criteria
WHILE @@FETCH_STATUS=0
BEGIN

select @criteriaAdd = criteria 
from @TableDataCriteria where table_name = @sqlTable

set @sqlCol = REPLACE(REPLACE(REPLACE(@sqlCol, '"', ''), '[', ''), ']', '')

set @sql = 'SELECT @res = (SELECT ' + @sqlCol + ' FROM [dbo].[' + @sqlTable + ']'

if LEN(ISNULL(@CriteriaAdd,'')) > 0
begin
if LEN(ISNULL(@Criteria,'')) = 0
set @Criteria = @CriteriaAdd
else
set @Criteria = @Criteria + ' AND ' + @CriteriaAdd
end

if LEN(ISNULL(@Criteria,'')) > 0
set @sql = @sql + ' WHERE ' + @Criteria

set @sql = @sql + ' FOR JSON PATH, ROOT(''' + @sqlTable + '''))'

EXEC sp_executeSql @sql, N'@res nvarchar(max) output', @res output

if len(@json) > 0 set @json = @json + ', '
set @json = @json + @res

FETCH NEXT FROM cursor1 INTO  @sqlTable, @sqlCol, @criteria
END
CLOSE cursor1
DEALLOCATE cursor1

SELECT '[' + @json + ']' AS ReturnData
end
GO

CREATE PROCEDURE [dbo].[ExecAction]
@Action char(3),
@TableName varchar(50),
@TableColumn TableColumn readonly,
@CurrentId int
as
begin
SET NOCOUNT ON

DECLARE @TriggerColumns TriggerColumn 

DECLARE @trigger_id INT,  @trigger_table VARCHAR(50), @trigger_action_type CHAR(3), @trigger_link_exp NVARCHAR(4000)
DECLARE cursor1 CURSOR LOCAL FOR
select t.trigger_id, t.trigger_table, t.trigger_action_type, t.trigger_link_exp
from t_trigger t
where t.action_table = @TableName and t.action_type = @Action
order by t.trigger_id
OPEN cursor1
FETCH NEXT FROM cursor1 INTO @trigger_id, @trigger_table, @trigger_action_type, @trigger_link_exp
WHILE @@FETCH_STATUS=0
BEGIN
delete from @TriggerColumns
insert into @TriggerColumns (trigger_table, trigger_column, trigger_exp)
select t.trigger_table, ti.trigger_column, ti.trigger_exp
from t_trigger_item ti
join t_trigger t on t.trigger_id = ti.trigger_id
where t.trigger_id = @trigger_id

exec ExecTrigger @CurrentId, @trigger_link_exp, @trigger_action_type, @trigger_table, @TriggerColumns

FETCH NEXT FROM cursor1 INTO  @trigger_id, @trigger_table, @trigger_action_type, @trigger_link_exp
END
CLOSE cursor1
DEALLOCATE cursor1
end
GO

CREATE PROCEDURE [dbo].[RunAction]
@json nvarchar(max)
as
begin
SET NOCOUNT ON

declare @Operation varchar(20)
declare @TableName varchar(50)
declare @Name nvarchar(50)
declare @Action char(3)

declare @CurrentId int
declare @TableColumn TableColumn
declare @TableData TableData
declare @TableDataCriteria TableData
declare @TriggerColumn TriggerColumn

declare @ActionTable varchar(50)
declare @TriggerTable varchar(50)
declare @ActionType char(3)
declare @TriggerActionType char(3)
declare @TriggerLinkExp nvarchar(4000)

SELECT @Operation = Operation, @Action = Action, @Name = Name, 
  @ActionTable = ActionTable, @TriggerTable = TriggerTable, @ActionType = ActionType, @TriggerActionType = TriggerActionType, @TriggerLinkExp = TriggerLinkExp
FROM OPENJSON(@json, N'$')
WITH (
Operation   VARCHAR(20)       N'$.Operation',
Action      VARCHAR(3)        N'$.Action',
Name        NVARCHAR(50)      N'$.Name',
ActionTable VARCHAR(50)       N'$.ActionTable',
TriggerTable VARCHAR(50)      N'$.TriggerTable',
ActionType VARCHAR(3)         N'$.ActionType',
TriggerActionType VARCHAR(3)  N'$.TriggerActionType',
TriggerLinkExp NVARCHAR(4000) N'$.TriggerLinkExp'
)

if (@Operation in ('ChangeModel', 'ChangeData'))
begin
SELECT @TableName = TableName, @CurrentId = CurrentId
FROM OPENJSON(@json, N'$.Data')
WITH (
TableName   VARCHAR(50)     N'$.TableName',
CurrentId   INT             N'$.CurrentId'
)

insert into @TableColumn(column_name, column_val, column_type)
SELECT ColumnName, ColumnValue, 0
FROM OPENJSON(@json, N'$.Data.Columns')
WITH (
ColumnName  VARCHAR(50)     N'$.Column',
ColumnValue NVARCHAR(max)   N'$.Value'
)
end
if (@Operation = 'ChangeLogic')
begin
insert into @TriggerColumn(trigger_table, trigger_column, trigger_exp)
SELECT @TriggerTable, ColumnName, ColumnExp
FROM OPENJSON(@json, N'$.Data.Columns')
WITH (
ColumnName  VARCHAR(50)   N'$.Column',
ColumnExp NVARCHAR(max)   N'$.Exp'
)

insert into t_trigger (name, action_table, action_type, trigger_table, trigger_action_type, trigger_link_exp)
values (@Name, @ActionTable, @ActionType, @TriggerTable, @TriggerActionType, @TriggerLinkExp)
declare @trigger_id int = scope_identity()

insert into t_trigger_item (trigger_id, trigger_column, trigger_exp)
select @trigger_id, trigger_column, trigger_exp
from @TriggerColumn
end
if (@Operation in ('GetData'))
begin
insert into @TableDataCriteria(table_name, table_columns, criteria)
SELECT json_value(value, '$.TableName'), '', json_value(value, '$.Criteria')
FROM OPENJSON(@json, N'$.Tables') t

select @json = getdata_json
from t_getdata where getdata_name = @Name

insert into @TableData(table_name, table_columns, criteria)
SELECT json_value(value, '$.TableName'), json_query(value, '$.Columns'), json_value(value, '$.Criteria')
FROM OPENJSON(@json, N'$.Tables') t
end
if (@Operation in ('GetDataModel'))
begin
insert into @TableData(table_name, table_columns, criteria)
SELECT json_value(value, '$.TableName'), json_query(value, '$.Columns'), json_value(value, '$.Criteria')
FROM OPENJSON(@json, N'$.Tables') t
end


if (@Operation = 'ChangeModel')
begin
exec UpdateModel @Action, @TableName, @TableColumn
end
if (@Operation = 'ChangeData')
begin
exec UpdateData @Action, @TableName, @TableColumn, @CurrentId output
exec ExecAction  @Action, @TableName, @TableColumn, @CurrentId

-- select @CurrentId -- RETURN ID
end
if (@Operation = 'GetDataModel')
begin
exec GetData @TableData

if not exists(select 1 from t_getdata where getdata_name = @Name)
insert into t_getdata(getdata_name, getdata_json) values (@Name, @json)
else
update t_getdata set getdata_json = @json where getdata_name = @Name
end
if (@Operation = 'GetData')
begin
exec GetData @TableData, @TableDataCriteria
end
end
GO


воскресенье, 26 марта 2017 г.

Back-End .NET / SQL Developer
Shargorodskyi Evgenyi



e-mail: portalevgfed@gmail.com
skype: evgenyi.shargorodskyi
phone: +38 (067) 7127614

Linkedin


Skills:


.NET (C#, VB.NET)
ASP.NET MVC, WEB API
WinForms, WPF, Silverlight, WCF
EF, NHibernate, Spring.NET

MSSQL, SSRS
BI - OLAP, SSAS, ProClarity
Oracle
MySql

Microsoft Bot Framework
Language Understanding (LUIS)

(Basic knowledge) - Angularjs 2, TypeScript, jQuery, HTML, CSS

Java, Spring, Hibernate
GWT, GXT, Servlet/JSP, Spring MVC


Professional Experience:


2017-... - "Trinetix LLC"
.NET / SQL developer
C#, MSSQL

2015-2017 - "Caspio"
www.caspio.com - Online database applications
.NET / SQL developer
C#, VB.NET, MSSQL, WEB API REST

2013-2015 - "SoftServe"
Renaissance Capital Bank - Internal banking systems
.NET developer
C#, ASP.NET MVC, MSSQL, Oracle, MSMQ, WCF

2010-2013 - "Luxoft"
OTP Bank - Internal banking systems
.NET / Java developer
C#, WinForms, WPF, ASP.NET, Silverlight, Oracle, WCF, Spring.NET, NHibernate, DevExpress
Java, Spring, Hibernate, GWT, GXT, Servlet/JSP, Spring MVC, MySql

2006-2009 - "Overia"
Overia Tourism - CRM for tourism
.NET / SQL developer
C#, WinForms, DevExpress, MSSQL, BI, OLAP, SSAS, ProClarity


Education:


Dniprodzerzhynsk State Technical University
Engineer of Radio Engineering, Radioelectronic Devices and Communications

English - read and write technical documentation


My projects:


  • R console (Angular 2, ASP.NET Core RESTful WEB API)




пятница, 24 марта 2017 г.

R console



Мой e-mail: portalevgfed@gmail.com
Skype: evgenyi.shargorodskyi

Телефон: +38 (067) 7127614



Используемые технологии


Angularjs 2

ASP.NET Core RESTful WEB API

MSSQL



О проекте


Онлайн консоль для работы / изучения языка R.



Ссылка


http://portal-e.com:3000

В данный момент все хостится на моем домашнем ПК, который бывает выключенным. 
Напишите если есть проблема с доступом.



Архитектура проекта:



Просто напишите R скрипт и нажмите "RUN SCRIPT".
В консоле для удобства есть справочник скриптов c примерами скриптов на языке R.

Выберите скрипт, нажмите "Load script", и затем "RUN SCRIPT":

Результат выполнения скрипта (график если результатом скрипта есть график):

Результат выполнения скрипта (текст если результатом скрипта есть некие данные):

Справочник скриптов:

Редактор скрипта:

Краткий мануал по R:

воскресенье, 8 мая 2016 г.

Portal-E - Прототип


Мой e-mail: portalevgfed@gmail.com

Skype: evgenyi.shargorodskyi

Телефон: +38 (067) 7127614




Используемые технологии


Java, GWT, GXT, Spring, Hibernate, MySql


Есть желание переписать это все дело с гораздо большими возможностями на 
Angular 4 + (Express.js or WEB API) + MongoDB, но нет времени :) 

Идея проекта


Сделать легко конфигурируемый конструктор учетных систем.
Работающий на всех популярных браузерах различных устройств.
Простой пользователь может очень просто создать учетную систему для своих нужд.
Пользователю не нужны знания программирования, дизайна, ...

Система работает в двух режимах:
1. Режим конфигурации (доступен для администраторов)
    В этом режиме создается "структура" системы (справочники, документы, отчеты).
2. Пользовательский режим (режим для всех пользователей)
    В этом режиме работают все пользователи в системе.
Подробнее режимы описаны ниже.

Прототип


Текущая реализация является прототипом.
Еще не реализованы: безопасность, локализация, ... возможны ошибки.


Ссылки


Пример конфигурации "Склад" - http://portal-e.com/bport/BPortMain.html?account=B3302189
Новая конфигурацияhttp://portal-e.com/bport/BPortMain.html?mode=config

В данный момент все хостится на моем домашнем ПК, который бывает выключенным. Напишите если есть проблема с доступом.


Видео - Новая конфигурация




Видео - Конфигурация "Склад"





Скриншот конфигурации "Склад":



Вход в систему


Вход в систему: логин test и пароль test


Создание новой конфигурации/аккаунта


Для создания нового аккаунта (конфигурации) нажмите кнопку "Get New Account"

введите "Name" вашего аккаунта и нажмите "Close". 
При желании данные аккаунта с ссылками придут на ваш e-mail, для этого введите "E-mail" и нажмите "Send e-mail".

Система работает в двух режимах:
- режим конфигурации (установлена галочка "Configurator" при логине)
- пользовательский режим (не установлена галочка "Configurator" при логине)

Для перехода между режимами просто нажмите клавишу F5 (обновить страницу) и установите галочку "Configurator" в нужное положение. 

Для удобства можно указать аккаунт и режим в адресной строке в параметрах "account" и "mode" соответственно:
http://portal-e.com/bport/BPortMain.html?account=E2EE90A1&mode=config

Режим конфигурации (доступен для администраторов)
Режим для работы с метаданными конфигурации:
- создание/редактирование/удаление таблиц, регистров, отчетов

Пользовательский режим (режим для всех пользователей)
На картинке "пустой" аккаунт






















Создание справочника/документа


Создание очень простого справочника "Client" с полями "Name" и "Address".

- Заходим в режиме конфигурации
- Выбирает "Tables" (таблицы)
- Нажимаем "New"
- Вводим "Name" Client
- Нажимаем "Add new item" (кнопка слева - зеленый плюс)

- Вводим имя поля "Name"
- Нажимаем "Save and Close"

- Таким же образом добавить поле "Address"

 - И нажимаем "Save and Close"


























Все.
Теперь заходим в пользовательском режиме и можем работать с справочником "Client"

 Добавляем новых клиентов (нажать кнопку "New")
 Вот в итоге что получилось

























Работа с документами, регистрами, ... 

. . .
. . .

Создание полноценной конфигурации

. . .
. . .