https://github.com/coverlet-coverage/coverlet

https://docs.microsoft.com/en-us/dotnet/core/testing/unit-testing-code-coverage

Primeiros passos com o framework de cobertura de código por testes unitários.

Com o VS 2019, ao criar um “projeto” de teste unitário com MSTest, o pacote coverlet.collector é incluído automaticamente.

Então, basta executar o seguinte comando no package manager console:

dotnet test --collect:"XPlat Code Coverage"

O resultado é a geração de arquivos XML com dados de cobertura em subdiretórios dos “projetos” de testes unitários.

Neste arquivo, o que devo ver? Esta ainda é a primeira vez que usei, então é uma resposta imatura, mas vamos lá.

A primeira tag:

<coverage line-rate=”0.1595″ branch-rate=”0.2727″ version=”1.9″ timestamp=”1594815785″ lines-covered=”154″ lines-valid=”965″ branches-covered=”24″ branches-valid=”88″>

Ou seja, está mostrando que:

(1) line-rate=”0.1595″ => apenas 15,95% das linhas foram executadas nos testes.

(2) lines-covered=”154″ lines-valid=”965″ => mesma informação, mas em termos absolutos, ou seja, 154 de 965 das linhas executáveis foram realmente executadas nos testes.

(3) branch-rate=”0.2727″ => apenas 27,27% das caminhos possíveis do código foram executados.

(4) branches-covered=”24″ branches-valid=”88″ => mesma informação, mas em termos absolutos, ou seja, 24 de 88 caminhos possíveis do código foram realmente executados nos testes.

A seguir, vêm os detalhes destas estatísticas discrimnadas por classe, mostrando todas as linhas e indicando se foram executadas ou não.

As informações são organizadas por classe, método e linha.

O mais interessante são as tags line com hits=”0″. Exemplo:

<line number=”96″ hits=”0″ branch=”False” />

Preste atenção nestas linhas, pois indicam o que ainda falta testar.

Um caso clássico é um if. Exemplo:

if(this.MyProperty == someValue)
DoSomething(); //hits > 0
else
DoAnotherThing(); //hits = 0

Ou seja, falta um teste em que this.MyProperty != someValue.

Finalmente, para visualizar o resultado de uma forma mais amigável (e muito interessante), é necessário um gerador de relatório. Então, instale o pacote, gere o relatório e abra-o no navegador:

dotnet tool install -g dotnet-reportgenerator-globaltool

reportgenerator
"-reports:Path\To\TestProject\TestResults\{guid}\coverage.cobertura.xml"
"-targetdir:coveragereport"
-reporttypes:Html

Mas o que seria realmente espetacular seria isto integrado visualmente dentro do VisualStudio. Talvez já exista isso, vamos ver.

Abordagem #3:

Alguém pode gostar de DataSet‘s e DataTable‘s.

public DataTable Report3(DateTime start, DateTime end)
{
    DataTable tab = new DataTable();
    for (int i = 0; i  n.DataRecebimento >= start)
        .Where(n => n.DataRecebimento  n.Fornecedor)
        .ThenInclude(f => f.PessoaCredor)
        .ThenInclude(p => p.EnderecoPessoa)
        .ThenInclude(e => e.Cidade)
        .ThenInclude(c => c.EstadoMunicipio)
        .ThenInclude(uf => uf.PaisEstado)
        .SelectMany(n => n.Produtos)
        .Include(item => item.MaterialItem)
        .Include(item => item.UnidadeCompra)
        .Select(item => tab.Rows.Add(
            item.NFEItem.Numero,
            item.NFEItem.Serie,
            item.NFEItem.DataRecebimento,
            item.NFEItem.Fornecedor.PessoaCredor.CNP,
            item.NFEItem.Fornecedor.PessoaCredor.Nome,
            item.NFEItem.Fornecedor.PessoaCredor.EnderecoPessoa.Cidade.Nome,
            item.NFEItem.Fornecedor.PessoaCredor.EnderecoPessoa.Cidade.EstadoMunicipio.Sigla,
            item.NFEItem.Fornecedor.PessoaCredor.EnderecoPessoa.Cidade.EstadoMunicipio.PaisEstado.Nome,
            item.Sequencial,
            item.MaterialItem.Abreviatura,
            item.MaterialItem.Codigo,
            item.UnidadeCompra.Codigo
        ))
        .ToList();

    return tab;
}

A “vantagem” de retornar um DataTable é não criar uma classe de modelo específico para o relatório. Quer dizer, se por acaso você fosse obrigado a gerar DataTable para a visualização do relatório, então você não ia querer primeiro criar e preencher uma classe de modelo para depois ter que copiar os dados para um DataTable.

Uma coisa que fica feita é que o ToList retorna uma coisa inútil. Usei-o para executar a query, mas talvez tenha outra forma, mas desconheço no momento.

Abordagem #2:

public List Report2(DateTime start, DateTime end)
{
    return this.Entities
    .AsNoTracking()
    .Where(n => n.DataRecebimento >= start)
    .Where(n => n.DataRecebimento  n.Fornecedor)
    .ThenInclude(f => f.PessoaCredor)
    .ThenInclude(p => p.EnderecoPessoa)
    .ThenInclude(e => e.Cidade)
    .ThenInclude(c => c.EstadoMunicipio)
    .ThenInclude(uf => uf.PaisEstado)
    .SelectMany(n => n.Produtos)
    .Include(item => item.MaterialItem)
    .Include(item => item.UnidadeCompra)
    .Select(item => new RefactorMe()
    {
        Numero = item.NFEItem.Numero,
        Serie = item.NFEItem.Serie,
        DataRecebimento = item.NFEItem.DataRecebimento,
        CNP = item.NFEItem.Fornecedor.PessoaCredor.CNP,
        NomeFornecedor = item.NFEItem.Fornecedor.PessoaCredor.Nome,
        Cidade = item.NFEItem.Fornecedor.PessoaCredor.EnderecoPessoa.Cidade.Nome,
        UF = item.NFEItem.Fornecedor.PessoaCredor.EnderecoPessoa.Cidade.EstadoMunicipio.Sigla,
        Pais = item.NFEItem.Fornecedor.PessoaCredor.EnderecoPessoa.Cidade.EstadoMunicipio.PaisEstado.Nome,
        SequenciaItem = item.Sequencial,
        Material = item.MaterialItem.Abreviatura,
        CodigoMaterial = item.MaterialItem.Codigo,
        Unidade = item.UnidadeCompra.Codigo
    })
    .ToList();
}

Note que para esse caso foi criada uma classe especificamente com o modelo final específico para o relatório. Isso cria uma complicação para desenhar as camadas da aplicação.

public class RefactorMe 
{
    public int Numero;
    public string Serie;
    public DateTime DataRecebimento;
    public string CNP;
    public string NomeFornecedor;
    public string Cidade;
    public string UF;
    public string Pais;
    public int SequenciaItem;
    public string Material;
    public string CodigoMaterial;
    public string Unidade;
}

Em compensação, a query tem um join bizarro de NotaFiscalEntrada para ItemNFEProduto e depois outro de ItemNFEProduto para NotaFiscalEntrada. Veja query abaixo.

Isso poderia ser evitado, declarando um DbSet para ItemNFEProduto e partindo dele ao invés de partir da NotaFiscalEntrada. Deve ter outro jeito também, mas não é o propósito deste post.

Query gerada:

SELECT [n0].[Numero], [n0].[Serie], [n0].[DataRecebimento], [p].[CNP], [p].[Nome] AS [NomeFornecedor],
[m].[Nome] AS [Cidade], [e0].[Sigla] AS [UF], [p0].[Nome] AS [Pais], [i].[Sequencial] AS [SequenciaItem], [m0].[Abreviatura] AS [Material],
[m0].[Codigo] AS [CodigoMaterial], [u].[Codigo] AS [Unidade]
FROM[NotaFiscalEntrada] AS[n]
INNER JOIN[ItemNFEProduto] AS[i] ON[n].[AutoId] = [i].[NFEItem]
LEFT JOIN[NotaFiscalEntrada] AS[n0] ON[i].[NFEItem] = [n0].[AutoId]
LEFT JOIN[Credor] AS[c] ON[n0].[Fornecedor] = [c].[AutoId]
LEFT JOIN[Pessoa] AS[p] ON[c].[PessoaCredor] = [p].[AutoId]
LEFT JOIN[Endereco] AS[e] ON[p].[EnderecoPessoa] = [e].[AutoId]
LEFT JOIN[Municipio] AS[m] ON[e].[Cidade] = [m].[AutoId]
LEFT JOIN[Estado] AS[e0] ON[m].[EstadoMunicipio] = [e0].[AutoId]
LEFT JOIN[Pais] AS[p0] ON[e0].[PaisEstado] = [p0].[AutoId]
LEFT JOIN[Material] AS[m0] ON[i].[MaterialItem] = [m0].[AutoId]
LEFT JOIN[Unidade] AS[u] ON[i].[UnidadeCompra] = [u].[AutoId]
WHERE([n].[DataRecebimento] >= @__start_0) AND([n].[DataRecebimento] <= @__end_1)',N'@__start_0 datetime2(7),@__end_1 datetime2(7)',@__start_0='2019 – 01 – 01 00:00:00',@__end_1='2019 – 01 – 31 00:00:00'

Abordagem para gerar relatório com o Entity Framework Core 3.1.

Preocupações: desempenho, modelo pronto para relatório.

Estratégia:

  • Evitar inúmeras consultas no banco.
  • Desabilitar change tracking.
  • Evitar lazy loading.
  • Obter todos os dados em uma única query.

Nota: nem sempre obter todos os dados de uma vez é melhor e lembre-se de checar os índices.

Abordagem #1:

public List<NotaFiscalEntrada> Report1(DateTime start, DateTime end)
{
  return this.Entities //é o DbSet
    .AsNoTracking()
    .Where(n => n.DataRecebimento >= start)
    .Where(n => n.DataRecebimento <= end)
    .Include(n => n.Fornecedor)
    .ThenInclude(f => f.PessoaCredor)
    .ThenInclude(p => p.EnderecoPessoa)
    .ThenInclude(e => e.Cidade)
    .ThenInclude(c => c.EstadoMunicipio)
    .ThenInclude(uf => uf.PaisEstado)
    .Include(n => n.Produtos).ThenInclude(item => item.MaterialItem)
    .Include(n => n.Produtos).ThenInclude(item => item.UnidadeCompra)
    .ToList();
}

O resultado é uma lista de entidades do modelo (domínio) da aplicação. Quem chamar o método, vai poder navegar nas entidades envolvidas na query. Se tentar acessar outra, vai resultar dar erro, pois a lazy initialization não funciona quando se usa AsNoTracking nesta versão (acho que antes funcionava) (tomando como premissa que a lazy initialization foi ativada no DbContext).

O ruim desta abordagem é que o resultado ainda não é o modelo pronto para o relatório. Ou melhor, há alguns casos que se pode usar as classes do .NET diretamente como fonte de dados, como no Crystal Reports, mas em geral prefiro um resultado mais flat. As próximas abordagens postadas fazem isso.

Detalhe importante: os dois Include‘s de n.Produtos são necessários para incluir as duas navegações irmãs (dois joins). Esse macete é necessário somente a partir do segundo nível de navegação. Isso não gera dois joins. Veja query gerada abaixo.

Query gerada:

dica: use SQL Profiler ou AnjSQL para ver a query.

SELECT [n].[AutoId], [n].[TelosRgDt], [n].[TelosRgUs], [n].[DataEmissao], [n].[DataRecebimento], [n].[Fornecedor], [n].[TelosUpDt], [n].[TelosUpUs], [n].[Numero], [n].[Serie], [c].[AutoId], [c].[Codigo], [c].[TelosRgDt], [c].[TelosRgUs], [c].[TelosUpDt], [c].[TelosUpUs], [c].[PessoaCredor], [p].[AutoId], [p].[CNP], [p].[TelosRgDt], [p].[TelosRgUs], [p].[EnderecoPessoa], [p].[TelosUpDt], [p].[TelosUpUs], [p].[Nome], [p].[RazaoSocial], [e].[AutoId], [e].[Bairro], [e].[CEP], [e].[Cidade], [e].[Complemento], [e].[TelosRgDt], [e].[TelosRgUs], [e].[TelosUpDt], [e].[TelosUpUs], [e].[Logradouro], [e].[Numero], [m].[AutoId], [m].[Codigo], [m].[CodigoIBGE], [m].[TelosRgDt], [m].[TelosRgUs], [m].[EstadoMunicipio], [m].[TelosUpDt], [m].[TelosUpUs], [m].[Nome], [e0].[AutoId], [e0].[CodigoIBGE], [e0].[TelosRgDt], [e0].[TelosRgUs], [e0].[TelosUpDt], [e0].[TelosUpUs], [e0].[Nome], [e0].[PaisEstado], [e0].[Sigla], [p0].[AutoId], [p0].[Codigo], [p0].[CodigoBACEN], [p0].[TelosRgDt], [p0].[TelosRgUs], [p0].[TelosUpDt], [p0].[TelosUpUs], [p0].[Nome], [t].[AutoId], [t].[TelosRgDt], [t].[TelosRgUs], [t].[TelosUpDt], [t].[TelosUpUs], [t].[MaterialItem], [t].[NFEItem], [t].[Quantidade], [t].[Sequencial], [t].[UnidadeCompra], [t].[ValorTotal], [t].[ValorUnitario], [t].[AutoId0], [t].[Abreviatura], [t].[Codigo], [t].[TelosRgDt0], [t].[TelosRgUs0], [t].[Descricao], [t].[TelosUpDt0], [t].[TelosUpUs0], [t].[AutoId1], [t].[Codigo0], [t].[TelosRgDt1], [t].[TelosRgUs1], [t].[Descricao0], [t].[TelosUpDt1], [t].[TelosUpUs1]
FROM [NotaFiscalEntrada] AS [n]
LEFT JOIN [Credor] AS [c] ON [n].[Fornecedor] = [c].[AutoId]
LEFT JOIN [Pessoa] AS [p] ON [c].[PessoaCredor] = [p].[AutoId]
LEFT JOIN [Endereco] AS [e] ON [p].[EnderecoPessoa] = [e].[AutoId]
LEFT JOIN [Municipio] AS [m] ON [e].[Cidade] = [m].[AutoId]
LEFT JOIN [Estado] AS [e0] ON [m].[EstadoMunicipio] = [e0].[AutoId]
LEFT JOIN [Pais] AS [p0] ON [e0].[PaisEstado] = [p0].[AutoId]
LEFT JOIN (
SELECT [i].[AutoId], [i].[TelosRgDt], [i].[TelosRgUs], [i].[TelosUpDt], [i].[TelosUpUs], [i].[MaterialItem], [i].[NFEItem], [i].[Quantidade], [i].[Sequencial], [i].[UnidadeCompra], [i].[ValorTotal], [i].[ValorUnitario], [m0].[AutoId] AS [AutoId0], [m0].[Abreviatura], [m0].[Codigo], [m0].[TelosRgDt] AS [TelosRgDt0], [m0].[TelosRgUs] AS [TelosRgUs0], [m0].[Descricao], [m0].[TelosUpDt] AS [TelosUpDt0], [m0].[TelosUpUs] AS [TelosUpUs0], [u].[AutoId] AS [AutoId1], [u].[Codigo] AS [Codigo0], [u].[TelosRgDt] AS [TelosRgDt1], [u].[TelosRgUs] AS [TelosRgUs1], [u].[Descricao] AS [Descricao0], [u].[TelosUpDt] AS [TelosUpDt1], [u].[TelosUpUs] AS [TelosUpUs1]
FROM [ItemNFEProduto] AS [i]
LEFT JOIN [Material] AS [m0] ON [i].[MaterialItem] = [m0].[AutoId]
LEFT JOIN [Unidade] AS [u] ON [i].[UnidadeCompra] = [u].[AutoId]
) AS [t] ON [n].[AutoId] = [t].[NFEItem]
WHERE ([n].[DataRecebimento] >= @__start_0) AND ([n].[DataRecebimento] <= @__end_1)
ORDER BY [n].[AutoId], [t].[AutoId]’,N’@__start_0 datetime2(7),@__end_1 datetime2(7)’,@__start_0=’2019-01-01 00:00:00′,@__end_1=’2019-01-31 00:00:00′

Windows 10, Monitor de Recuros (Resources Monitor)

É isso que você está procurando para saber qual processo está usando um arquivo.

É isso que você está procurando para saber porque um arquivo está sendo usado por um processo.

Calendário no Power BI

Posted: December 5, 2019 in Uncategorized

let
Source = List.Dates(StartDate, Length, #duration(1,0,0,0)),
#”Converted to Table” = Table.FromList(Source, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
#”Changed Type” = Table.TransformColumnTypes(#”Converted to Table”,{{“Column1″, type date}}),
#”Renamed Columns” = Table.RenameColumns(#”Changed Type”,{{“Column1”, “Data”}}),
StartDate = #date(2014, 1, 1),
EndDate = DateTime.Date(DateTime.LocalNow()),
Length = Duration.Days(EndDate – StartDate),
#”Inserted Year” = Table.AddColumn(#”Renamed Columns”, “Year”, each Date.Year([Data]), Int64.Type),
#”Inserted Month” = Table.AddColumn(#”Inserted Year”, “Month”, each Date.Month([Data]), Int64.Type),
#”Inserted Days in Month” = Table.AddColumn(#”Inserted Month”, “Days in Month”, each Date.DaysInMonth([Data]), Int64.Type),
#”Inserted Month Name” = Table.AddColumn(#”Inserted Days in Month”, “Month Name”, each Date.MonthName([Data]), type text),
#”Inserted Day of Week” = Table.AddColumn(#”Inserted Month Name”, “Day of Week”, each Date.DayOfWeek([Data]), Int64.Type),
#”Inserted Day Name” = Table.AddColumn(#”Inserted Day of Week”, “Day Name”, each Date.DayOfWeekName([Data]), type text),
#”Inserted YearName” = Table.AddColumn(#”Inserted Day Name”, “YearMonth”, each [Year] * 100 + [Month]),
#”Inserted Id” = Table.AddColumn(#”Inserted YearName”, “MonthSequentialId”, each ([Year] – Date.Year([Data]))*12 + Date.Month([Data]))
//It is important to add an ID column that supports the creation of custom time intelligence formulas.
//https://exceleratorbi.com.au/build-reusable-calendar-table-power-query/)
in
#”Inserted Id”

Aprendi aqui:

Build a Reusable Calendar Table with Power Query

Posted: December 3, 2019 in Uncategorized

========================================================
SOBRE USO DE CORDOVA PARA GERAR APLICATIVO ANDROID (APK)
========================================================

————————————————–
Sobre a preparação do ambiente de desenvolvimento:
————————————————–

https://cordova.apache.org/docs/en/3.1.0/guide/cli/index.html
https://cordova.apache.org/docs/en/latest/guide/platforms/android/index.html
https://jdk.java.net/12/
https://gradle.org/install/
https://developer.android.com/studio/index.html

npm install -g cordova
cordova create ProjectFolder-cordova com.wordpress.ideiasdefenestradas.anApp anApp
cd ProjectFolder-cordova
Run this to check your current set of platforms: $ cordova platforms ls
cordova platform add android
cordova build
cordova emulate android

—————————-
Como gerar aplicativo (APK):
—————————-

cd ProjectFolder\an-app
ng build –configuration=production –base-href .
cd ..\..
del .\ProjectFolder-cordova\www\*.js
xcopy .\ProjectFolder\an-app\dist\an-app .\ProjectFolder-cordova\www /E /Y
cd .\ProjectFolder-cordova\

REM editar index.html, remover scripts type=module, remover atributos nomodule das tags “script”

cordova build –debug

REM nao pode ser: cordova build –release
REM cordova run android –emulator (tem que abrir o emulador antes)

copy D:\projetos\ProjectFolder-cordova\platforms\android\app\build\outputs\apk\debug\app-debug.apk \\gama\aplicacoes\zbuffer\carvaoweb.apk

REM o dispositivo Android que for rodar tem que ficar em modo desenvolvedor
REM baixar o APK no dispositivo e clicar nele (pode ser via um link na web)

Para permitir download APK no IIS:
https://serverfault.com/questions/501562/configure-iis-7-0-to-download-apk/501564
Open the Internet Information Service (IIS) Manager -> Properties
Click MIME types
New -> type Extension “.apk” and MIME type “application/vnd.android.package-archive”
Click Ok and Apply

https://developers.google.com/web/tools/chrome-devtools/remote-debugging/
View at Medium.com

—————-
Troubleshooting:
—————-

1) Failed to load module script: The server responded with a non-JavaScript MIME type of “”. …
R) Remover casos de <script … type=”module”> do index.html

2) Blanck screen
R) Remover do index.html:
casos de <script … type=”module”> do index.html
atributos nomodule das tags <script>

3) ERR_CLEARTEXT_NOT_PERMITTED
R1) Usar HTTPS (não testei ainda)
R2) https://stackoverflow.com/questions/45940861/android-8-cleartext-http-traffic-not-permitted
https://stackoverflow.com/questions/54752716/why-am-i-seeing-neterr-cleartext-not-permitted-errors-after-upgrading-to-cordo
AndroidManifest.xml: <application android:usesCleartextTraffic=”true”></application>

4) 403 Forbidden
R1) Parece CORS por causa do header origin=file://
property name=”AllowedOrigin” value=”http://localhost:4200&#8243;
resolvi refazendo o projeto seguindo:
https://docs.microsoft.com/en-us/aspnet/web-api/overview/security/enabling-cross-origin-requests-in-web-api
alguns outros links que pesquisei e que podem ter sido úteis ou não:
https://stackoverflow.com/questions/41113179/cordova-android-app-cant-post-403-forbidden
https://stackoverflow.com/questions/24973290/cordova-post-request-forbidden-403-not-reaching-dispatcher-servlet/24976158#24976158
https://impeccabletester.wordpress.com/2016/09/19/postman-dealing-with-cors/
https://stackoverflow.com/questions/20079813/how-to-make-cors-authentication-in-webapi-2
https://www.c-sharpcorner.com/article/making-proxy-request-in-angular-fix-cors-issue-in-angular-application/
https://stackoverflow.com/questions/37398695/cors-cordova-issues-with-access-control-allow-origin
https://github.com/apache/cordova-plugin-whitelist
https://stackoverflow.com/questions/49937905/access-control-allow-origin-error-using-cordova/57489240#57489240
https://stackoverflow.com/questions/19095777/how-to-support-http-options-verb-in-asp-net-mvc-webapi-application
https://docs.microsoft.com/en-us/aspnet/web-api/overview/security/enabling-cross-origin-requests-in-web-api
https://stackoverflow.com/questions/3595515/xmlhttprequest-error-origin-null-is-not-allowed-by-access-control-allow-origin
https://github.com/ionic-team/cordova-plugin-ionic-webview
https://stackoverflow.com/questions/25914071/cors-cordova-angularjs-http-and-file-confusion/25914800
https://stackoverflow.com/questions/21297169/cors-and-phonegap-apps
cordova plugin add cordova-plugin-wkwebview-file-xhr

5) HTTP Error 500.19 0x80070003
R) Deploy na pasta errada. Estava em c:\metalsoft\carvaoweb ao inves de c:\metalsoft\carvaoweb\api
http://mitchfincher.blogspot.com/2014/08/0x80070003-http-error-50019-internal.html

6) HTTP 500.23 0x80070032
R) https://stackoverflow.com/questions/16897262/http-error-500-23-internal-server-error
<system.webServer>
<validation validateIntegratedModeConfiguration=”false”/>
</system.webServer>

7) HTTP Erro 403 Forbidden (apenas quando o emulador do android ou o tablet tentava acessar o serviço no servidor do cliente)
R) Fortinet estava barrando a aplicação e o Guilherme resolveu.
Nota: somente uma vez apareceu uma resposta em HTML que dizia que o Fortinet barrou uma aplicação que violava as políticas de segurança do cliente.

8) HTTP Erro 500 no servidor do cliente. Reproduzi no gama e o erro é: The specified policy origin ‘file://’ is invalid. It cannot end with a forward slash.
Causa: <property name=”AllowedOrigin” value=”file://” />
R) Quando a origem é “file://”, basta desabilitar o CORS do servidor e está tudo certo. No asp.net, tirar EnableCors().
https://stackoverflow.com/questions/21297169/cors-and-phonegap-apps

Esse cara teve a manha: fez um dashboard com PowerBI para mostrar o log do próprio PowerBI (desktop).

Rui Romano Blog - A place to share my thoughts

In this post I will show you how to analyse Power BI Desktop diagnostic trace files in a more visual way than notepad Smile

First you need to collect some diagnostics by enabling tracing on Power BI Desktop, go to: File –> Options –> Diagnostics –> Enable Tracingimage

If you click on “Open Traces folder”:

image

It will open the trace folder with all the trace logs:

image

PS – Trace log are only generated after you test your power bi report, do some refresh and interactions first to create the trace logs

Now to analyse these logs you could off course open them in notepad:

image

But is not very easy to read, so what better way to process and visualize this huge amount of text data???

So I created a Power BI Desktop to process and visualize the trace logs, that will allow you to quickly visualize things like:

  • Errors
  • Duration of…

View original post 32 more words

Certificado para testes

Posted: March 8, 2019 in Uncategorized

Criando certificado para testes (self-signed).

New-SelfSignedCertificate -Type CodeSigningCert -Subject "CN=COMPANY CA, 0=CORPORATION, C=US" -KeyUsage DigitalSignature -KeyLength 2048 -NotAfter (Get-Date).AddMonths(120) -FriendlyName cool-name

Note Certificate Thumbprint.

$pwd = ConvertTo-SecureString -String <Your Password> -Force -AsPlainText

Export-PfxCertificate -cert "Cert:\LocalMachine\My\<Certificate Thumbprint>" -FilePath nomearquivo.pfx -Password $pwd

https://stackoverflow.com/questions/48509114/how-to-create-a-working-trusted-and-or-self-signed-certificate-for-a-windows-10

Coloque arquivo .pfx no projeto como “embedded resource”.

private X509Certificate2 MockCertificado()
{
  byte[] embeddedCert;
  var thisAssembly = System.Reflection.Assembly.GetAssembly(this.GetType());
  using (var certStream = thisAssembly.GetManifestResourceStream("namespace.nomearquivo.pfx"))
  {
    embeddedCert = new byte[certStream.Length];
    certStream.Read(embeddedCert, 0, (int)certStream.Length);
  }

  var certificado = new X509Certificate2(embeddedCert, "pwd");
  return certificado;
}

Aqui vai um passo a passo para vencer alguns problemas para desenvolver o REINF.

1) Você vai precisar de um certificado digital.

Se não tiver um, dizem que é possível criar um certificado de teste gratuitamente conforme este site:
https://social.technet.microsoft.com/wiki/pt-br/contents/articles/28398.criando-certificado-digital-para-teste-gratis.aspx

  • Para testes unitários da assinatura (sem transmitir), é possível criar um certificado de teste com o Visual Studio:
  • https://msdn.microsoft.com/en-us/library/bfsktky3(v=VS.100).aspx
  • Visual Studio 2017 Developer Command Prompt
  • makecert -pe -sky signature -sv reinf.pvk reinf.cer
  • pvk2pfx -pvk reinf.pvk -pi Sa113s -spc reinf.cer -pfx reinf.pfx -f

2) Gere o XML do evento R1000 sem identação e espaços inúteis e com declaração do XML (a tag “?xml”). Ele tem que ficar assim (ocultei o CNPJ e os conteúdos gigantes das tags SignatureValue e X509Certificate):

<?xml version=”1.0″ encoding=”utf-8″?><Reinf xmlns=”http://www.reinf.esocial.gov.br/schemas/evtInfoContribuinte/v1_02_00″><evtInfoContri id=”ID1XXXXXXXX0001XX2017112409110500000″><ideEvento><tpAmb>3</tpAmb><procEmi>1</procEmi><verProc>4.4.0</verProc></ideEvento><ideContri><tpInsc>1</tpInsc><nrInsc>XXXXXXXX0001XX</nrInsc></ideContri><infoContri><inclusao><idePeriodo><iniValid>2017-11</iniValid></idePeriodo><infoCadastro><classTrib>99</classTrib><indEscrituracao>1</indEscrituracao><indDesoneracao>0</indDesoneracao><indAcordoIsenMulta>0</indAcordoIsenMulta><indSitPJ>0</indSitPJ><contato><nmCtt>DADOS FICTICIOS</nmCtt><cpfCtt>11111111111</cpfCtt><foneFixo>3132920485</foneFixo></contato></infoCadastro></inclusao></infoContri></evtInfoContri><Signature xmlns=”http://www.w3.org/2000/09/xmldsig#”><SignedInfo><CanonicalizationMethod Algorithm=”http://www.w3.org/TR/2001/REC-xml-c14n-20010315&#8243; /><SignatureMethod Algorithm=”http://www.w3.org/2001/04/xmldsig-more#rsa-sha256&#8243; /><Reference URI=”#ID1XXXXXXXX0001XX2017112409110500000″><Transforms><Transform Algorithm=”http://www.w3.org/2000/09/xmldsig#enveloped-signature&#8221; /><Transform Algorithm=”http://www.w3.org/TR/2001/REC-xml-c14n-20010315&#8243; /></Transforms><DigestMethod Algorithm=”http://www.w3.org/2001/04/xmlenc#sha256&#8243; /><DigestValue>9JQowxNxZJ8/h4kF2mrnx556AmymB0SVN2+fNYxSJ7g=</DigestValue></Reference></SignedInfo><SignatureValue>(ocultei)</SignatureValue><KeyInfo><X509Data><X509Certificate>(ocultei)</X509Certificate></X509Data></KeyInfo></Signature></Reinf>

2.1) Repare no atributo id em:
<evtInfoContri id=”ID1XXXXXXXX0001XX2017112409110500000″>
Esse id deve ser gerado conforme a regra descrita no manual do REINF: ID + Tipo de Inscrição + CPNJ + DATA/HORA + SEQUENCIAL.
A versão original do manual indica apresenta letra maiúscula (Id), mas o certo é tudo minúsculo (id).
O id é muito importante, pois é usado para indicar qual tag será assinada, ou seja, qual tag será usada para computar o DigestValue.

3) Assine o XML do evento. Algumas pessoas dizem que é necessário remover a declaração XML (XmlDeclaration), mas nos meus testes conclui que isso não é necessário.

Para assinar, é importante montar corretamente a URI referenciando o atributo id e usar os algoritmos corretos definidos pelo manual do REINF.

É importante garantir que o XML assinado esteja codificado em UTF8. Antes de descobrir isso, estava ocorrendo este erro:

<codigo>MS0017</codigo><descricao>Assinatura do evento inválida. Assinatura Digital do documento XML é inválida</descricao>

Segue código em C#.

public XmlDocument Assinar(XmlDocument documento, string id)
{
if (documento == null)
throw new ArgumentNullException(“documento”);
if(this.Certificado == null)
throw new InvalidOperationException(“certificado”);

Reference reference = new Reference();
reference.Uri = “#” + id; //referencia a tag que será usada para computar a assinatura

XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform();
reference.AddTransform(env);

XmlDsigC14NTransform c14 = new XmlDsigC14NTransform();
reference.AddTransform(c14);

reference.DigestMethod = “http://www.w3.org/2001/04/xmlenc#sha256&#8221;;

KeyInfo keyInfo = new KeyInfo();

keyInfo.AddClause(new KeyInfoX509Data(this.Certificado));

XmlDocument documentoUTF8 = ReconstruirComoUTF8(documento);

SignedXml signature = new SignedXml(documentoUTF8);
signature.SigningKey = this.Certificado.PrivateKey;
signature.AddReference(reference);
signature.KeyInfo = keyInfo;
signature.SignedInfo.SignatureMethod = “http://www.w3.org/2001/04/xmldsig-more#rsa-sha256&#8221;;
signature.ComputeSignature();

documentoUTF8.DocumentElement.AppendChild(documentoUTF8.ImportNode(signature.GetXml(), true));
return documentoUTF8;
}

private XmlDocument ReconstruirComoUTF8(XmlDocument documento)
{
//sem isso dava: Assinatura do evento inválida. Assinatura Digital do documento XML é inválida
var bytes = System.Text.Encoding.UTF8.GetBytes(documento.OuterXml);
var rawDocumentoUTF8 = System.Text.Encoding.UTF8.GetString(bytes);
XmlDocument documentoUTF8 = new XmlDocument();
documentoUTF8.LoadXml(rawDocumentoUTF8);
return documentoUTF8;
}

4) Agora monte o XML final. Segue abaixo uma solicitação completa (ocultei o CNPJ e os conteúdos gigantes das tags SignatureValue e X509Certificate):

<?xml version=”1.0″ encoding=”utf-8″?>
<soap:Envelope
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance&#8221;
xmlns:xsd=”http://www.w3.org/2001/XMLSchema&#8221;
xmlns:soap=”http://schemas.xmlsoap.org/soap/envelope/&#8221;
xmlns:sped=”http://sped.fazenda.gov.br/”&gt;
<soap:Header></soap:Header>
<soap:Body>
<sped:ReceberLoteEventos>
<sped:loteEventos>
<Reinf xmlns=”http://www.reinf.esocial.gov.br/schemas/envioLoteEventos/v1_02_00″&gt;
<loteEventos><evento id=”ID1XXXXXXXX0001XX2017112409110500000″><Reinf xmlns=”http://www.reinf.esocial.gov.br/schemas/evtInfoContribuinte/v1_02_00″><evtInfoContri id=”ID1XXXXXXXX0001XX2017112409110500000″><ideEvento><tpAmb>3</tpAmb><procEmi>1</procEmi><verProc>4.4.0</verProc></ideEvento><ideContri><tpInsc>1</tpInsc><nrInsc>XXXXXXXX0001XX</nrInsc></ideContri><infoContri><inclusao><idePeriodo><iniValid>2017-11</iniValid></idePeriodo><infoCadastro><classTrib>99</classTrib><indEscrituracao>1</indEscrituracao><indDesoneracao>0</indDesoneracao><indAcordoIsenMulta>0</indAcordoIsenMulta><indSitPJ>0</indSitPJ><contato><nmCtt>DADOS FICTICIOS</nmCtt><cpfCtt>11111111111</cpfCtt><foneFixo>3132920485</foneFixo></contato></infoCadastro></inclusao></infoContri></evtInfoContri><Signature xmlns=”http://www.w3.org/2000/09/xmldsig#”><SignedInfo><CanonicalizationMethod Algorithm=”http://www.w3.org/TR/2001/REC-xml-c14n-20010315&#8243; /><SignatureMethod Algorithm=”http://www.w3.org/2001/04/xmldsig-more#rsa-sha256&#8243; /><Reference URI=”#ID1XXXXXXXX0001XX2017112409110500000″><Transforms><Transform Algorithm=”http://www.w3.org/2000/09/xmldsig#enveloped-signature&#8221; /><Transform Algorithm=”http://www.w3.org/TR/2001/REC-xml-c14n-20010315&#8243; /></Transforms><DigestMethod Algorithm=”http://www.w3.org/2001/04/xmlenc#sha256&#8243; /><DigestValue>9JQowxNxZJ8/h4kF2mrnx556AmymB0SVN2+fNYxSJ7g=</DigestValue></Reference></SignedInfo><SignatureValue>(ocultei)</SignatureValue><KeyInfo><X509Data><X509Certificate>(ocultei)</X509Certificate></X509Data></KeyInfo></Signature></Reinf></evento></loteEventos>
</Reinf>
</sped:loteEventos>
</sped:ReceberLoteEventos>
</soap:Body>
</soap:Envelope>

4.1) Usei o id do lote igual ao id do evento. O manual não fala muito sobre o id do lote. Até achei que tinha que ser diferente, mas testei igual e diferente (mudando o sequencial) e não deu problema em nenhum dos dois casos.

4.2) Não testei se faz diferença, mas em C# usei: xmlEventos.PreserveWhitespace = false;

4.3) Se tiver fazendo a requisição, confira o SOAPAction e não esqueça de adicionar o certificado:

request.Headers.Add(“SOAPAction”, @”http://sped.fazenda.gov.br/RecepcaoLoteReinf/ReceberLoteEventos&#8221;);
request.ClientCertificates.Add(this.Certificado);

5) Eis um exemplo de retorno com sucesso:

<s:Envelope xmlns:s=”http://schemas.xmlsoap.org/soap/envelope/”><s:Body xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance&#8221; xmlns:xsd=”http://www.w3.org/2001/XMLSchema”><ReceberLoteEventosResponse xmlns=”http://sped.fazenda.gov.br/”><ReceberLoteEventosResult><Reinf xmlns=”http://www.reinf.esocial.gov.br/schemas/retornoLoteEventos/v1_02_00″><retornoLoteEventos id=”ID84B630F4297DEA9331BBFEAD87B54C3C”><ideTransmissor><IdTransmissor>XXXXXXXX0001XX</IdTransmissor></ideTransmissor><status><cdStatus>0</cdStatus><descRetorno>SUCESSO</descRetorno></status><retornoEventos><evento id=”ID1XXXXXXXX0001XX2017112408574800001″><Reinf xmlns=”http://www.reinf.esocial.gov.br/schemas/retornoEvento/v1_02_00″><retornoEvento id=”ID2041693781″><ideContrib><tpInsc>1</tpInsc><nrInsc>XXXXXXXX0001XX</nrInsc></ideContrib><dadosRecepcaoEvento><dhProcessamento>2017-11-24T08:57:51.2433996-02:00</dhProcessamento><tipoEvento>1000</tipoEvento><idEvento>ID1XXXXXXXX0001XX2017112408574800000</idEvento><hash>K/73wNsvr6uJEhimftYrKIGSX8BO5bG9b5bR5mV3g+Q=</hash></dadosRecepcaoEvento><status><cdRetorno>0</cdRetorno><descRetorno>SUCESSO</descRetorno></status><dadosReciboEntrega><numeroRecibo>3865-05-1000-1711-3865</numeroRecibo></dadosReciboEntrega></retornoEvento><Signature xmlns=”http://www.w3.org/2000/09/xmldsig#”><SignedInfo><CanonicalizationMethod Algorithm=”http://www.w3.org/TR/2001/REC-xml-c14n-20010315″/><SignatureMethod Algorithm=”http://www.w3.org/2001/04/xmldsig-more#rsa-sha256″/><Reference URI=”#ID2041693781″><Transforms><Transform Algorithm=”http://www.w3.org/2000/09/xmldsig#enveloped-signature”/><Transform Algorithm=”http://www.w3.org/TR/2001/REC-xml-c14n-20010315″/></Transforms><DigestMethod Algorithm=”http://www.w3.org/2001/04/xmlenc#sha256″/><DigestValue>3Lmi/BnmN4Dbchw3JiP1NLv9MFRs5S9oMc9k7VI3bPA=</DigestValue></Reference></SignedInfo><SignatureValue>(ocultei)</SignatureValue><KeyInfo><X509Data><X509Certificate>(ocultei)</X509Certificate></X509Data></KeyInfo></Signature></Reinf></evento></retornoEventos></retornoLoteEventos><Signature xmlns=”http://www.w3.org/2000/09/xmldsig#”><SignedInfo><CanonicalizationMethod Algorithm=”http://www.w3.org/TR/2001/REC-xml-c14n-20010315″/><SignatureMethod Algorithm=”http://www.w3.org/2001/04/xmldsig-more#rsa-sha256″/><Reference URI=”#ID84B630F4297DEA9331BBFEAD87B54C3C”><Transforms><Transform Algorithm=”http://www.w3.org/2000/09/xmldsig#enveloped-signature”/><Transform Algorithm=”http://www.w3.org/TR/2001/REC-xml-c14n-20010315″/></Transforms><DigestMethod Algorithm=”http://www.w3.org/2001/04/xmlenc#sha256″/><DigestValue>dxC3pujf3xMabNEUTTYaACPPougGdCbKtvdBOJZMgi0=</DigestValue></Reference></SignedInfo><SignatureValue>(ocultei)</SignatureValue><KeyInfo><X509Data><X509Certificate>(ocultei)</X509Certificate></X509Data></KeyInfo></Signature></Reinf></ReceberLoteEventosResult></ReceberLoteEventosResponse></s:Body></s:Envelope&gt;

6) Eis um exemplo de retorno com erro:

<s:Envelope xmlns:s=”http://schemas.xmlsoap.org/soap/envelope/”><s:Body xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance&#8221; xmlns:xsd=”http://www.w3.org/2001/XMLSchema”><ReceberLoteEventosResponse xmlns=”http://sped.fazenda.gov.br/”><ReceberLoteEventosResult><Reinf xmlns=”http://www.reinf.esocial.gov.br/schemas/retornoLoteEventos/v1_02_00″><retornoLoteEventos id=”ID5368CFE13BA3BCF5A995B5B520F43139″><ideTransmissor><IdTransmissor>XXXXXXXX0001XX</IdTransmissor></ideTransmissor><status><cdStatus>0</cdStatus><descRetorno>SUCESSO</descRetorno></status><retornoEventos><evento id=”ID1XXXXXXXX0001XX2017112316423400001″><Reinf xmlns=”http://www.reinf.esocial.gov.br/schemas/retornoEvento/v1_02_00″><retornoEvento id=”ID1050126439″><ideContrib><tpInsc>1</tpInsc><nrInsc>XXXXXXXX0001XX</nrInsc></ideContrib><dadosRecepcaoEvento><dhProcessamento>2017-11-23T16:42:40.4774646-02:00</dhProcessamento><tipoEvento>1000</tipoEvento><idEvento>ID1XXXXXXXX0001XX2017112316423400000</idEvento><hash>dHME03m8CaWtebKFFhYoRdWZAfnkRmwJfpoOCh/f2sI=</hash></dadosRecepcaoEvento><status><cdRetorno>1</cdRetorno><descRetorno>ERRO</descRetorno><dadosRegistroOcorrenciaEvento><ocorrencias><tipo>1</tipo><localizacaoErroAviso/><codigo>MS0017</codigo><descricao>Assinatura do evento inválida. Assinatura Digital do documento XML é inválida</descricao></ocorrencias></dadosRegistroOcorrenciaEvento></status></retornoEvento><Signature xmlns=”http://www.w3.org/2000/09/xmldsig#”><SignedInfo><CanonicalizationMethod Algorithm=”http://www.w3.org/TR/2001/REC-xml-c14n-20010315″/><SignatureMethod Algorithm=”http://www.w3.org/2001/04/xmldsig-more#rsa-sha256″/><Reference URI=”#ID1050126439″><Transforms><Transform Algorithm=”http://www.w3.org/2000/09/xmldsig#enveloped-signature”/><Transform Algorithm=”http://www.w3.org/TR/2001/REC-xml-c14n-20010315″/></Transforms><DigestMethod Algorithm=”http://www.w3.org/2001/04/xmlenc#sha256″/><DigestValue>12DpOHfYUsX3eSQfiv2DmsmdXZ1ZxY3qzl4gU3sIfX8=</DigestValue></Reference></SignedInfo><SignatureValue>(ocultei)</SignatureValue><KeyInfo><X509Data><X509Certificate>(ocultei)</X509Certificate></X509Data></KeyInfo></Signature></Reinf></evento></retornoEventos></retornoLoteEventos><Signature xmlns=”http://www.w3.org/2000/09/xmldsig#”><SignedInfo><CanonicalizationMethod Algorithm=”http://www.w3.org/TR/2001/REC-xml-c14n-20010315″/><SignatureMethod Algorithm=”http://www.w3.org/2001/04/xmldsig-more#rsa-sha256″/><Reference URI=”#ID5368CFE13BA3BCF5A995B5B520F43139″><Transforms><Transform Algorithm=”http://www.w3.org/2000/09/xmldsig#enveloped-signature”/><Transform Algorithm=”http://www.w3.org/TR/2001/REC-xml-c14n-20010315″/></Transforms><DigestMethod Algorithm=”http://www.w3.org/2001/04/xmlenc#sha256″/><DigestValue>2pAeOQQ5kr4E32SnaQ5gj6sPk8ZvriAey0IKB6/R9hU=</DigestValue></Reference></SignedInfo><SignatureValue>(ocultei)</SignatureValue><KeyInfo><X509Data><X509Certificate>(ocultei)</X509Certificate></X509Data></KeyInfo></Signature></Reinf></ReceberLoteEventosResult></ReceberLoteEventosResponse></s:Body></s:Envelope&gt;

7) Se você receber um retorno com a mensagem “O servidor remoto retornou um erro: (500) Erro Interno do Servidor.”, verifique a tag “faultstring” dentro do XML retornado para saber a mensagem de erro de verdade.