Skip to content
No description, website, or topics provided.
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.idea
Libraries/src
data
postman
static/template
.gitignore
Dockerfile
LICENSE
README.md
captura_de_tela.png
db.js
docker-compose.yml
docker-entrypoint.sh
init.js
main.go
mongodb_data.json
untreatedData.js

README.md

test_server

Este servidor é uma demonstração em go para a Neoway e tem as seguintes funcionalidades:

  • Upload de arquivos csv
  • Atualização de dados a partir do arquivo csv
  • Busca por nome de empresa e código postal
  • Busca por nome de empresa e código postal usando lógica fuzzy para o nome da empresa.
  • Uso de script javascript para adicionar novas funcionalidades de forma simples e rápida ao servidor.

A lógica fuzzy faz com que a busca possa ser feita por um texto que contenha as letras formadoras de uma frase, desde que as letras estejam na mesma ordem. Por exemplo, a empresa de nome "TOLA SALES GROUP" pode ser achada pelas letras "tlsg", uma vez que as letras aparecem no texto na mesma ordem.

Requisitos:

  • Docker
  • Docker-compose

Instalação

Para instalar este servidor, basta abrir o terminal e digitar o comando

docker-compose up --build

O docker-compose instalará o MongoDB, o servidor de demonstração e o Mongo Express.

Servidor demonstração

Servidor escrito em golang rodando na porta 9090.

Para acessar o servidor, basta abrir o navegador e acessar o endereço http://localhost:9090/status e o servidor deverá responder com um json informando que está ok

endpoint metodo descrição
/status get retorna um json informando o status do servidor
/upload post permite o upload do arquivo q1_catalog.csv
/update post permite o upload do arquivo q2_clientData.csv
/findCompany get faz a busca, estilo like do nome da empresa pelo código postal
/findCompanyFuzzy get faz a busca fuzzy do nome da empresa pelo código postal
/memory get mostra o status da memória usada pelo golang
/customJs get executa um código JavaScript contido no banco de dados
/admin/reloadConfig get recarrega as configurações do servidor
/runQuery get executa uma query do banco de dados e devolve a saída para o servidor

JavaScript e Golang

Este servidor usa o projeto duktape escrito em C++, feito para permitir a dispositivos móveis rodar código JavaScript com pouco mais de 700kb de memória de forma rápida e segura.

A finalidade de se rodar código JavaScript é permitir ao responsável pelos dados fazer scripts sem depender da intervenção de terceiros no servidor e sem necessitar reiniciar o mesmo.

Basicamente, as funcionalidades de entradas e saída de dados são escritas em Golang e dão controle total ao acesso concebido ao responsável pelos dados, impedindo o acesso total ao banco de dados, por exemplo.

Como este é só um exemplo rápido e não deu tempo de fazer algo mais elaborado, o exemplo contido no banco de dados contém o código.

(function() {

    // adiciona uma nova rota ao servidor com query de banco e dados via url amigavel
    addRouteToGetData( 'viewCode', 'javaScript_code', {"name": "#name#"}, '^.*?/(?P<name>[a-zA-Z0-9_]+)$', {} );

}).call(this);

Este script permite ao responsável pelos dados criar um novo endpoint no servidor, onde este endpoint tem o nome viewCode, acessa a coleção de dados javaScript_code e executa a query {"name": "#name#"} no banco de dados, onde o valor da variável #name# vem na forma de URL amigável e é definida pela expressão regular ^.*?/(?P[a-zA-Z0-9_]+)$. Por final, as chaves retornadas pelo MongoDB são filtradas pelo objeto {}.

http://localhost:9090/runQuery/viewCode/onLoad

campo descrição
runQuery endpoint
viewCode nome do endpoint adicionado via JavaScript
onLoad valor passado para a variável #name#

Para mais informações de como rodar JavaScript em um servidor Golang, visite o site do projeto duktape https://duktape.org/

O código JavaScript, va versão atual, pode ser editado on-line com o uso do ACE Editor e está sendo portado para o Monaco Editor por uma questão de qualidade.

Mongo Express

No endereço http://0.0.0.0:8081/ há um view de banco de dados para o MongoDB. Ele é meio estranho, mas, serve para ver o conteúdo do banco de forma relativamente eficiente para uma demonstração simples.

endpoints

/status

Informa aos programas de monitoramento o status do servidor em tempo real

/upload

Upload do arquivo q1_catalog.csv como solicitado no desafio.

Para mais informações, há um exemplo de como utilizar o servidor na pasta postman

O aplicativo pode ser baixado gratuitamente no site https://www.getpostman.com/

/update

Upload do arquivo q1_clientData.csv como solicitado no desafio.

Para mais informações, há um exemplo de como utilizar o servidor na pasta postman

O aplicativo pode ser baixado gratuitamente no site https://www.getpostman.com/

/findCompany

Faz uma busca no banco de dados por nome da empresa AND zipCode, onde o nome da empresa é uma busca estilo like com expressão regular.

Para mais informações, há um exemplo de como utilizar o servidor na pasta postman

O aplicativo pode ser baixado gratuitamente no site https://www.getpostman.com/

/findCompanyFuzzy

Faz uma busca no banco de dados por nome da empresa AND zipCode, onde o nome da empresa é uma busca fuzzy para texto.

Para mais informações, há um exemplo de como utilizar o servidor na pasta postman

O aplicativo pode ser baixado gratuitamente no site https://www.getpostman.com/

/memory

Devolve um json com as informações de memória do servidor

/customJs

Roda um arquivo JavaScript contido no banco de dados. Por falta de tempo, vou ficar devendo um exemplo melhor, mas, o script foi feito para permitir acesso controlado aos dados, impedindo ao responsável pelo script de apagar o banco de dados ou ter acesso a todas as coleções.

A ideia básica é permitir ao responsável pelos dados atender as suas necessidades pessoais sem ter de esperar por um desenvolvedor.

O motivo da escolha foi a qualidade do projeto em si e o fato do JavaScript ser muito bem documentado.

Os pontos negativos são, o desempenho, o consumo de memória e a versão do ECMAScript implementada. Por exemplo, let não funciona na versão atual.

/admin/reloadConfig

Foi um copiar e colar desnecessário no momento.

/runQuery

Permite a criação de endpoits com querys de banco em 5 minutos, porém, necessita ser melhorada para receber as funcionalidades como limit e skip por exemplo.

Flags

Flag Descrição
server Endereço do servidor e porta. Exemplo: ":9090"
mongo_server Endereço do banco de dados e porta. Exemplo: mongodb://localhost:27017
mongo_database Nome do banco de dados. Exemplo: neoway
timeZone Zona de tempo do servidor de data e hora

Falhas no código

  • Por uma falta de tempo, só há um teste de endpoint escrito
  • O pacote de banco de dados usado não contém uma interface{}. Motivo, já estava feito.
  • Há algumas constantes no código. Amanhã eu corrijo.
  • Poderia haver um tratamento melhor na validação dos dados contidos no arquivo CSV. Vou criar uma demonstração em JavaScript com o editor on-line.
  • Banco de dados está sem chaves, por puro esquecimento.
  • Não há responsabilidade com o dado original. ( Toda informação alterada deve ter um log e um meio de se recuperar a mesma )

Exemplo JavaScript

Adicionado em 24/01 as 12:20

(function() {
    var logGetApi = require('db');
    logGetApi.collection('collectionName');
    logGetApi.find({
        'query': {
            "preProcessEnd": { "$ne": 0 },
            "getProcessEnd": { "$ne": 0 },
            "parsed": false,
        },
        'collection': 'collectionName'
    });
  
    var logGetApiData = logGetApi.dataGetAll();
    var idList = [];
    var data;
  
    for(;;){
        data = logGetApi.dataGetNext();
        if( !data.empresa ){
            break;
        }
      
        var id = data.empresa;

        var pass = true;
        for( var index in idList ){
            if( idList[ index ] == id ){
                pass = false;
            }
        }
    
        if(pass === true){
            idList.push( id );
        }
    }

    for( var key in idList ){
        var companyToFindData = idList[key];

        var ud = require('untreatedData');
        
        var newData = require('db');

        // define o nome da coleção para salvar os dado
        newData.collection('collectionName2');

        // calcula a quantidade de itens encontrado
        var count = ud.count({ 
    	    query: { 
    		    "company": companyToFindData, 
        		"parsed": false 
        	}
        });

        // valores muito elevados podem fazer a execução do javascript ficar lenta
        const limit = 100;

        // laço para pegar todos os documentos
        for( i = 0, l = Math.ceil( count/limit ); i < l; i += 1 ){

            // find na coleção 'untreatedData'
            ud.find({
                'query': { 
            	    "company": companyToFindData, 
            	    "processLogCreated.parsed": false 
                },
                'limit': limit,
                'skip': i * limit,
            });

            for(;;){
                data = ud.dataGetNext();
                if( !data.md5 ){
                    break;
                }

                // dados dos empregados
                var employeeMatricula = data.employee.matricula;
                var employeeNome      = data.employee.nome;
                var employeePis       = data.employee.pis;
                var employeeId        = data.employee._id;

                // demais dados
                var daysInfos         = data.daysInfos;
                var shifts            = data.shifts;
                var company           = data.company;
                var totals            = data.totals;

                // laço para cada linha do daysInfos contido no documento do banco de dados
                for( var daysInfosCount = 0, daysInfosLength = daysInfos.length; daysInfosCount < daysInfosLength; daysInfosCount += 1 ){

                    // separa um day info
                    var daysInfosElement  = daysInfos[daysInfosCount];

                    // separa os elementos dos indicadores
                    var daysInfosSumTotal       = daysInfosElement.sumTotal;
                    var daysInfosPunches        = daysInfosElement.punches;
                    var daysInfosJornada        = daysInfosElement.jornada;
                    var daysInfosDia            = daysInfosElement.dia;
                    var daysInfosIsAbsentAllDay = daysInfosElement.isAbsentAllDay;
    
                    // insere no banco de dados
                    newData.upset({
      	                query: {
      		                company:	company,
      		                dia:        daysInfosDia,
      		                pis:        employeePis,
              		        matricula:	employeeMatricula,
              	        },
              	        update: {
      	        	        company:			company,
                            sumTotal:			daysInfosSumTotal,
                            punches:			daysInfosPunches,
                            shifts: 			shifts,
	                        jornadaDaysInfos:   daysInfosJornada,
	                        dia:            	daysInfosDia,
                            isAbsentAllDay: 	daysInfosIsAbsentAllDay,
                            //daysInfos:        daysInfosElement,
                            totals:             totals,
                            matricula:      	employeeMatricula,
                            nome:           	employeeNome,
	                        pis:            	employeePis,
                            employee_id:        employeeId,
              	        }
                    });

                    // testa e aborta em caso de erro
                    var error = newData.getLastErrorAsString();
                    if( error !== '' ){
                        // fixme: log
                        print('newData.insert.error: ' + error);
                        return;
                    }
                }
                ud.update({
                    "query": { "_id": data._id },
                    "update": { "$set": { "processLogCreated.parsed": true } }
                });

                var error = ud.getLastErrorAsString();
                if( error !== '' ){
                    print('ud.update.error: ' + error);
                    return;
                }
            }
        }
    }
}).call(this);

O código acima recuperava dados de uma determinada coleção, processava e populava outra coleção de dados de forma que o analista de dados não necessitava parar o servidor quando a necessidade dele mudava.

A edição dos dados é feita com ajuda do ACE edito, e várias funções feitas em Golang funcionam de forma transparente no JavaScript, como é o caso das funções db.

Basicamente, um struct Golang é passado por ponteiro para a máquina virtual, e em paralelo, um código JavaScript é ecrito para compatibilizar com autocompletar do editor.

O grande ponto da máquina virtual é que a entrada/saída de dados muda de acordo com a plataforma, então a mesma não faz parte do código original da mesma e isto impede o acesso a dados do servidor por código interpretado pela mesma, tornando a execução do código segura.

Quanto a quantidade máxima de memória consumida por um código, há um limitador no código C e o valor deve ser alterado antes da compilação da aplicação Golang.

O projeto duktape é mantido pela comunidade de software embarcado e é bem ativo, o que torna a sua escolha relativamente segura, tanto em relação a falhas quanto em relação a manutenção do código com o tempo. A última versão é de 04/08/2018.

Particularmente, eu sempre pego a última versão do github e compilo, mas, faz um tempo que não necessito fazer isto.

You can’t perform that action at this time.