Ir para o conteúdo principal

Análise técnica sobre a dependência do SID da API antiga do E-Estado

Data de elaboração

09/12/2022

Responsável pelo estudo

José Lucas da Silva Costa (Analista de Desenvolvimento Full-Stack)


Equipe do estudo

João Pedro Rocha Brito (Assessor)

Jônatas Neves Legal (Técnico em Tecnologia da Informação e Comunicação) 

José Henrique dos Santos Nogueira (Assessor)

Alvo E-estado
Origem

Melhoria: Descontinuação da API do e-estado

Objetivo

Migração da API do e-estado
Documentação Correlata Sem documentação.

Observações

Sem observações.

1. Glossário de Termos

  1. API Application Programming Interface (interface de Programação de Aplicações).
  2. KIBANA - Plugin de visualização de dados de fonte aberta para o Elasticsearch.
  3. SETIC - Superintendência Estadual de Tecnologia da Informação e Comunicação.
  4. SID - Sistema Integrado de Descanso.

2. Introdução


Atualmente o SID possui algumas comunicações com a API antiga do E-Estado e há a necessidade de efetuar a migração antes da descontinuação da API antiga. Para melhor análise do problema se faz necessário este estudo para planejar a migração com menor impacto.

3. Desenvolvimento


3.1. Levantamento das funcionalidades que ainda utilizam a API Antiga do E-Estado

A API a ser migrada do E-estado não possui o cenário favorável as mudanças e migração, pois foram feitas implementações específicas em cima de resultados específicos, em que a API nova não irá atender totalmente, vejamos os pontos código que possui dependências com o E-estado antigo:

using System;
using RestSharp;
using System.Linq;
using SID.API.Models;
using System.Threading.Tasks;
using SID.API.Models.Constants;
using SID.API.Repositories.CRUD;
using System.Collections.Generic;
using SID.API.Models.Construtores;
using SID.API.Models.DTO.Servidor;
using SID.API.Repositories.Interfaces;
using Microsoft.Extensions.Configuration;
using SID.API.Models.DTO.Servidor.EEstadoServidores;

namespace SID.API.Repositories
{
    public class ServidorRepository : ConsumirRepository, IServidorRepository
    {
        public ServidorRepository(IConfiguration config)
        {
            Url = new Uri(config["UrlEEstadoApi"]);
            UrlServidores = new Uri(config["UrlServidores"]);
            AuthorizationKey = config["ServidoresAccessKey"];
            Origin.Add("Origin", config["UrlOrigin"]);
        }

        private Uri Url { get; set; }
        private Uri UrlServidores { get; set; }
        public string AuthorizationKey { get; set; }
        public IDictionary<string, string> Origin { get; set; } = new Dictionary<string, string>();

        public async Task<Servidor> BuscarPorMatricula(string matricula)
        {
            var rota = $"api/movimentacoes/matricula/{matricula}";

            var resultado = await Buscar<List<ServidorDto>>(Url, rota, Method.GET);

            var servidorDTO = resultado.FirstOrDefault(x => x.DepartamentoPadrao);

            if (servidorDTO != null)
            {
                ParamentrosDoServidor paramentrosDoServidor = new ParamentrosDoServidor
                {
                    Nome = servidorDTO.Nome,
                    Cpf = servidorDTO.Cpf,
                    Matricula = servidorDTO.Matricula,
                    Cargo = servidorDTO.Cargo,
                    Departamento = servidorDTO.NomeDoDepartamento,
                    DepartamentoId = servidorDTO.CodigoDoDepartamento.GetValueOrDefault(),
                    CodigoDaUnidadeOrcamentaria = Convert.ToInt32(servidorDTO.codigoDaUnidadeGestoraDoDepartamento),
                    UnidadeOrcamentaria = servidorDTO.UnidadeOrcamentaria,
                    DataDeAdmissao = servidorDTO.DataDeAdmissao.Value,
                    AtividadePrivativaNoCargo = servidorDTO.AtividadePrivativaNoCargo
                };
                return new Servidor(paramentrosDoServidor);
            }
            return null;
        }

        public async Task<Servidor> BuscarPorMatriculaComOuSemDepartamentoPadrao(string matricula)
        {
            var rota = $"api/movimentacoes/matricula/{matricula}";

            var resultado = await Buscar<List<ServidorDto>>(Url, rota, Method.GET);

            var servidorDTO = resultado.FirstOrDefault();

            if (servidorDTO != null)
            {
                ParamentrosDoServidor paramentrosDoServidor = new ParamentrosDoServidor
                {
                    Nome = servidorDTO.Nome,
                    Cpf = servidorDTO.Cpf,
                    Matricula = servidorDTO.Matricula,
                    Cargo = servidorDTO.Cargo,
                    Departamento = servidorDTO.NomeDoDepartamento,
                    DepartamentoId = servidorDTO.CodigoDoDepartamento.GetValueOrDefault(),
                    CodigoDaUnidadeOrcamentaria = Convert.ToInt32(servidorDTO.codigoDaUnidadeGestoraDoDepartamento),
                    UnidadeOrcamentaria = servidorDTO.UnidadeOrcamentaria,
                    DataDeAdmissao = servidorDTO.DataDeAdmissao.Value,
                    AtividadePrivativaNoCargo = servidorDTO.AtividadePrivativaNoCargo
                };
                return new Servidor(paramentrosDoServidor);
            }
            return null;
        }

        public async Task<IList<ServidorDto>> BuscarServidor(string nomeCpf)
        {
            var rota = $"api/movimentacoes/nomeOuCpf/{nomeCpf}";

            var resultado = await Buscar<List<ServidorDto>>(Url, rota, Method.GET);

            return resultado.Where(x => x.DataDeRescisao == null && x.DepartamentoPadrao).OrderBy(x => x.Nome).ToList();
        }

        public async Task<List<ServidorDto>> BuscarServidoresDoDepartamento(int departamentoId)
        {
            var rota = $"api/movimentacoes/departamentoId/{departamentoId}";

            var resultado = await Buscar<List<ServidorDto>>(Url, rota, Method.GET);

            var res = resultado.Where(x => x.DataDeRescisao == null && x.DepartamentoPadrao).OrderBy(x => x.Nome).ToList();

            return res;
        }

        public async Task<List<ServidorDto>> BuscarServidoresDaUnidadeOrcamentaria(int unidadeOrcamentariaId)
        {
            var rota = $"api/movimentacoes/unidadeDoDepartamentoId/{unidadeOrcamentariaId}";

            var resultado = await Buscar<List<ServidorDto>>(Url, rota, Method.GET);

            return resultado.Where(x => x.DataDeRescisao == null && x.DataDeAdmissao != null && x.DepartamentoPadrao).OrderBy(x => x.Nome).ToList();
        }

        public async Task<List<ServidorDto>> BuscarProfessoresDaUnidadeOrcamentaria(int unidadeOrcamentariaId, params int[] listaDepartamentoId)
        {
            var rota = $"api/movimentacoes/servidoresComCargoDeProfessorEatividadePrivativaNoCargo/{unidadeOrcamentariaId}";

            var resultado = await Buscar<List<ServidorDto>>(Url, rota, Method.GET);
            List<ServidorDto> novaListaServidores = FiltrarBuscarDosProfessores(listaDepartamentoId, resultado);
            return novaListaServidores;
        }

        public async Task<List<ServidorDto>> PesquisarProfessoresDaUnidadeOrcamentaria(int unidadeOrcamentariaId, string pesquisa, params int[] listaDepartamentoId)
        {
            var rota = $"api/movimentacoes/servidoresComCargoDeProfessorEatividadePrivativaNoCargo/{unidadeOrcamentariaId}";

            var resultado = await Buscar<List<ServidorDto>>(Url, rota, Method.GET);
            List<ServidorDto> novaListaServidores = FiltrarBuscarDosProfessores(listaDepartamentoId, resultado, pesquisa);
            return novaListaServidores;
        }

        private static List<ServidorDto> FiltrarBuscarDosProfessores(int[] listaDepartamentoId, List<ServidorDto> resultado, string pesquisa = "")
        {
            var novaListaServidores = new List<ServidorDto>();
            if (listaDepartamentoId.All(departamentoId => departamentoId != 0))
            {
                foreach (var departamentoId in listaDepartamentoId)
                    novaListaServidores.AddRange(resultado.Where(x => x.CodigoDoDepartamento == departamentoId).ToList());

                if (!string.IsNullOrEmpty(pesquisa))
                    novaListaServidores = novaListaServidores.Where(x => x.Nome.Contains(pesquisa)).ToList();
            }
            else 
                novaListaServidores = resultado;

            return novaListaServidores;
        }

        public async Task<List<AssentamentoDto>> BuscarAssentamentosPorMatricula(string matricula)
        {
            var rota = $"api/assentamento/matricula/{matricula}";

            var resultado = await Buscar<List<AssentamentoDto>>(Url, rota, Method.GET);

            var assentamentosFiltrados = new List<AssentamentoDto>();
            var codigosAssentamentos = AssentamentosConstants.codigosAssentamentosEspeciais;

            assentamentosFiltrados.AddRange(resultado.Where(y => codigosAssentamentos.Any(x => x == y.Codigo)));
            return assentamentosFiltrados;
        }

        public async Task<AssentamentoDto> BuscarAssentamentoPorMatricula(string matricula, string codigo, DateTime dataInicial)
        {
            var rota = $"api/assentamento/matricula/{matricula}";

            var resultado = await Buscar<List<AssentamentoDto>>(Url, rota, Method.GET);

            return resultado.FirstOrDefault(x => x.DataInicial.GetValueOrDefault().Date == dataInicial.Date && x.Codigo.Contains(codigo));
        }

        public async Task<ListaDeMatriculasDoServidorDto> BuscarPorCpf(string cpf)
        {
            var rota = $"?cpf={cpf}";

            var resultado = await BuscarComHeaders<ListaDeMatriculasDoServidorDto>(UrlServidores, rota, Method.GET, AuthorizationKey, Origin.FirstOrDefault());

            return resultado;
        }

        public async Task<ListaDeMatriculasDoServidorDto> BuscarServidoresDaUnidadeOrcamentariaPelaRotaPaginada(int unidadeOrcamentariaId, int pagina, double limit)
        {
            var rota = $"?unidade_trabalho_id={unidadeOrcamentariaId}&page={pagina}&apenas_ativos=true&limit=${Convert.ToInt32(limit)}";
            return await BuscarComHeaders<ListaDeMatriculasDoServidorDto>(UrlServidores, rota, Method.GET, AuthorizationKey, Origin.FirstOrDefault());
        }

        public async Task<DetalhesDoServidorDto> BuscarFichaFuncionalDoServidor(string matricula)
        {
            var rota = $"matricula/{matricula}/ficha-funcional";
            return await BuscarComHeaders<DetalhesDoServidorDto>(UrlServidores, rota, Method.GET, AuthorizationKey, Origin.FirstOrDefault());
        }

        public async Task<List<DepartamentoDto>> BuscarDetalhesDoServidor(int servidorId)
        {
            var rota = $"{servidorId}";
            var detalhesDoServidor = await BuscarComHeaders<DepartamentosDoServidorDto>(UrlServidores, rota, Method.GET, AuthorizationKey, Origin.FirstOrDefault());

            var departamentos = detalhesDoServidor.Departamentos.Select(x => x.Departamento).ToList();

            departamentos.ForEach(x => x.Padrao = detalhesDoServidor.Departamentos.First(y => y.Departamento.Id.Equals(x.Id)).Padrao);
            return departamentos;
        }
    }
}

Percebemos pelo código acostado que os métodos que dependem do E-Estado antigo são centralizados, então provável que a nova API seja implementada sem dificuldades, o único problema é na verdade a falta de sincronismo entre as funcionalidades, nem todas as funções que o E-estado antigo fazia estão acopladas na nova.

 

3.2. Complexidade de cada funcionalidade

A API a ser migrada do E-estado não possui o cenário de testes, principalmente o de carga que pode medir o desempenho da mesma, desta forma se faz necessário também os testes de API funcionais, que podem ajudar neste cenário onde está ocorrendo problemas de desempenho, principalmente se o mesmo problema vim na API nova que será migrada do E-estado. 

Testes de API funcionais são equivalentes a testes de unidade para software: uma maneira de garantir que a API retorne a saída desejada para uma determinada entrada, situação esta que ainda não possui nos projetos que o TITÃS mantém. Esses testes podem ser executados em todos os ambientes, desde o computador pessoal de um desenvolvedor até ambientes de teste até o sistema de produção final.

É recomendável executá-los para verificar se a implantação não interrompe a funcionalidade. Quando a implantação funciona, os testes devem retornar os mesmos resultados em todos os lugares. Já os testes de API de carga, por outro lado, normalmente são executados em produção ou em um sistema equivalente. Isso ocorre porque as restrições não funcionais, como confiabilidade e capacidade de resposta, parecem diferentes sob várias condições do mundo real. O teste de API funciona de maneira semelhante ao teste de site, embora os testes de site possam incluir o comportamento do navegador do lado do cliente, enquanto os testes de API enviam apenas solicitações de rede.

3.3. Possíveis problemas

Quando uma API falha ou possui problemas de desempenho, essa falha reflete na SETIC. Os usuários finais e clientes provavelmente não reconhecerão que um terceiro pode ser o culpado. E dependendo da importância dessa API para um processo de transação, essa falha pode afetar seus resultados imediatamente. Encontrar problemas de desempenho apenas algumas semanas antes da data de lançamento de um sistema em produção de uma API de aplicativo, é uma ocorrência comum. Um tempo de alta resolução é preciso até frações de milissegundos. Essa precisão o torna ideal para produzir medições precisas de tempo. Cada medição medida na Performance API é um tempo de alta resolução. A API de alto desempenho faz parte da API de tempo de alta resolução. À medida que o aplicativo aumenta, ele pode enfrentar problemas de desempenho. Portanto, é melhor ter testes de carga/desempenho desde o início. A maioria das empresas de desenvolvimento de software tem equipes de teste dedicadas que realizariam testes de integração para garantir que não haja gargalos.

3.4. Valor agregado

A melhoria de desempenho em API está sendo amplamente utilizada na indústria de software para permitir a integração de múltiplas aplicaçAtualizações de formaplataformas eficiente.sempre Emborasão osbem aplicativosvindas, tenhamno sidocenário submetidosda SETIC melhoria inclusive as situações de manunteção, utilizar plataformas mais atualizadas tendem a muitosser testesmais manuaisfácil ea automatizadosmanunteção. antesEntão, será de muita serventia que as depedências do lançamento,SID ésejam possívelatualizadas queem surjam problemas na produção. Neste estudo técnico, vimos os problemas comuns encontrados, seguidos pelas práticas para minimizar os problemas.conjunto.

4. Conclusão


O presente ESTUDO TÉCNICO PRELIMINAR, elaborado pelos integrantes TÉCNICOS do time TITÃS, considerando a análise dos desafios técnicos envolvidos e citados, conclui PELA ABORDAGEM DOS IMPACTOS DA DESCONTINUAÇÃO DA API DO E-ESTADO, uma vez que foram considerados a análise técnica dos serviços envolvidos, principalmente potenciais problemas que afetem a disponibilidade e desempenho do serviço. Em complemento, os contratempos identificados são administráveis, pelo que RECOMENDAMOS A REVISÃO E SUBSTITUIÇÃO DA ANTIGA API PELA NOVA, uma vez que, os projetos podem parar de funcionar.

5. Referências