terça-feira, 26 de setembro de 2017

CRM-GT - Consumo de Web Api usando C#

Este artigo tem como objetivo documentar o uso de um importante recurso do meu sistema de gerenciamento com o cliente, o CRM-GT. Este recurso se chama Web Api, e serve como um [conector] entre o sistema CRMGT e qualquer sistema legado.

Esta é a URL principal de consumo REST: /api/CRMGT/QueryXML

Com este conector, você pode efetuar todo CRUD de operações. Portanto, segue abaixo o uso prático deste recurso, usando C#.

Busca do Token de acesso (OAuth)
var url = "https://crm-gt.com/api/CRMGT/GetToken";
url += "?orgName=CRMGT1";
url += "&clientId=+n8c++NABWZrkldVUaf0XYH2T2lG5xvUhZEVxFz46/4=__5@_";
url += "&clientSecret=7RclDz1Ilbp3jyOuXJNnyfaK3bW+MIt5fU0BL1f46to=__0@_";
var jToken = GetString(url);
var token = JsonConvert.DeserializeObject<JObject>(jToken).SelectToken("token").ToString();

Considerações importantes sobre o token.
- Ele expira a cada 3 horas.
- O [clientId] e o [clientSecret] são criados por Organização. O administrador da organização cria estas chaves e as disponibiliza para obtenção do Token.

Para Renovar o Token de acesso (OAuth)
var url = "https://crm-gt.com/api/CRMGT/RenewToken";
url += "?orgName=CRMGT1";
url += "&clientId=+n8c++NABWZrkldVUaf0XYH2T2lG5xvUhZEVxFz46/4=__5@_";
url += "&clientSecret=7RclDz1Ilbp3jyOuXJNnyfaK3bW+MIt5fU0BL1f46to=__0@_";
var jToken = GetString(url);
var token = JsonConvert.DeserializeObject<JObject>(jToken).SelectToken("token").ToString();

Como saber se o token de acesso expirou? Ao executar o endpoint /GetToken, o sistema retorna um atributo informando que o tempo expirou ("expires_in" : "0").


Para os métodos de CREATE e UPDATE, no momento da configuração da tag [fields/field] atributo [TYPE], informe os tipos correspondentes aos do [SQL SERVER].
Segue os principais:
bit
char
nchar
varchar
nvarchar
datetime
int
decimal
text
uniqueidentifier
Desta forma, se precisar adicionar um campo do tipo [string], informe [varchar].
Se precisar adicionar um campo do tipo [valor ou moeda], informe [decimal], e assim por diante.


Para efetuar um SELECT

1 - Monte o "SELECT" através de uma instrução XML:
string xmlSELECT = @"
<select>
	<organization name='{0}' />
	<table name='User' alias='tb1' />
	<fields>
		<field name='tb1.UserId' label='ID' />
		<field name='tb1.Name' />
		<field name='tb1.Email' />
		<field name='tb2.Name' />
		<field name='tb3.Name' />
	</fields>
	<join isLeftJoin='false' name='Status' alias='tb2' fieldFrom='statuscode' fieldTo='statuscode'>
		<filter type='and'>
			<condition field='tb2.name' operator='=' value='Ativo' />
		</filter>                     
	</join>
	<join isLeftJoin='true' name='salesorder' alias='tb3' fieldFrom='userid' fieldTo='ownerid'>
		<filter type='and'>
			<condition field='tb3.name' operator='like' value='%ped%' />
		</filter>                     
	</join>
	<filter type='and'>
		<condition field='tb1.name' operator='like' value='%usr%' />
	</filter>
	<order>
		<field name='tb1.Name ASC'/>
		<field name='tb1.Email DESC'/>
	</order>	
</select>";

O XML acima é a representação do SELECT-SQL abaixo:
SELECT tb1.UserId, tb1.Name, tb1.Email, tb2.Name, tb3.Name
FROM [User] tb1 
JOIN [Status] tb2 ON tb1.statuscode = tb2.statuscode AND tb2.name = 'Ativo'
LEFT JOIN [salesorder] tb3 ON TB1.UserId = tb3.ownerid AND tb3.name like '%ped%'
WHERE tb1.Name like '%usr%'
ORDER BY tb1.Name ASC, tb1.Email DESC

2 - Passe como parâmetro a informação de [Organização], conforme a sequência abaixo.
var xmlF = string.Format(xmlSELECT, "CRMGT1");

3 - Execute a instrução, chamando a Web Api:
var header = new Dictionary<string, object>();
header.Add("Authorization", $"Bearer {token}");

var content = new Dictionary<string, object>();
content.Add("Type", "GET");
content.Add("Caller", "My Function Caller Name");
content.Add("xmlContent", xmlF);

var result = ExecutePostAsync("https://crm-gt.com/api/CRMGT/QueryXML", content, header);

Segue abaixo métodos [GetString] e [ExecutePostAsync] que efetuam chamadas HTTP:
public static String GetString(String url)
{
   var urlContents = "";
   try
   {
      using (HttpClient client = new HttpClient())
      {
         urlContents = client.GetStringAsync(new Uri(url)).Result;
      }
   }
   catch (Exception ex)
   {
   }

   return urlContents;
}

public static string ExecutePostAsync(string url, Dictionary<string, object> content, Dictionary<string, object> headerList = null) {
	string result = "";

	try {
		using (HttpClient client = new HttpClient()) {
			if (headerList != null) {
				foreach (var item in headerList) {
					client.DefaultRequestHeaders.Add(item.Key, item.Value.ToString());
				}
			}

			if (content != null) {
				var multiPartContent = new MultipartFormDataContent();
				foreach (var item in content) {
					multiPartContent.Add(new StringContent(item.Value.ToString()), string.Format("\"{0}\"", item.Key));
				}

				var request = new HttpRequestMessage(HttpMethod.Post, url);
				request.Content = multiPartContent;
				result = client.SendAsync(request).Result.Content.ReadAsStringAsync().Result;
			}
		}
	}
	catch (Exception ex) {
	}

	return result;
}


Para efetuar um CREATE

1 - Monte o "CREATE" através de uma instrução XML:
string xmlINSERT = @"
    <insert>
        <organization name='{0}' />
        <table name='{1}' isNN='false' setInternalFields='true' setWorkflowTrigger='false' />
        <records>
            <record>
                <fields>
                    <field name='statuscode' type='int' value='998' />
                    <field name='name' type='varchar' value='test1...' />
                    <field name='ownerid' type='uniqueidentifier' lookuptable='user' lookupsearchfield='name' value='system' />
                </fields>
            </record>
            <record>
                <fields>
                    <field name='statuscode' type='int' value='999' />
                    <field name='name' type='varchar' value='test2...' />
                    <field name='ownerid' type='uniqueidentifier' value='9EC0ABA8-2947-4857-9698-1C43BBBFAB5B' />
                </fields>
            </record>
        </records>
    </insert>";

2 - Passe como parâmetro as informações de [Organização] e [tabela], conforme a sequência abaixo.
var xmlF = string.Format(xmlINSERT, "CRMGT1", "lead");

3 - Execute a instrução, chamando a Web Api:
var header = new Dictionary<string, object>();
header.Add("Authorization", $"Bearer {token}");

var content = new Dictionary<string, object>();
content.Add("Type", "SET");
content.Add("Caller", "My Function Caller Name");
content.Add("xmlContent", xmlF);

var result = ExecutePostAsync("https://crm-gt.com/api/CRMGT/QueryXML", content, header);

Para inserir dados em uma tabela N-N, adicione o atributo [isNN='true'] na tag xml [table], conforme exemplificado abaixo:
string xmlINSERT = @"
    <insert>
        <organization name='{0}' />
        <table name='{1}' isNN='true' />
        <records>
            <record>
                <fields>
                    <field name='pricelist_productid' type='uniqueidentifier' value='NEWID' />
                    <field name='pricelistid' type='uniqueidentifier' value='3BEF33F8-A159-4F77-964D-B820B8414E59' />
                    <field name='productid' type='uniqueidentifier' value='B091A317-4FF7-42BE-AD86-D77C22059E49' />
                </fields>
            </record>
            <record>
                <fields>
                    <field name='pricelist_productid' type='uniqueidentifier' value='NEWID' />
                    <field name='pricelistid' type='uniqueidentifier' value='3BEF33F8-A159-4F77-964D-B820B8414E59' />
                    <field name='productid' type='uniqueidentifier' value='6922B778-B4FF-4564-A992-E40A020C599F' />
                </fields>
            </record>
        </records>
    </insert>";

Se precisar definir uma regra antes da criação dos registros, você pode adicionar a seguinte tag [rules] depois da tag [table]:
<rules>
	<rule action="insert_ignoreifexists" actionoperator="AND">
		<fields>
			<field name="protocol" type="VarChar" />
			<field name="incidentNumber" type="Int" />
		</fields>
	</rule>
</rules>

Desta forma, através da regra [insert_ignoreifexists] você não permite a inclusão do registro se os campos especificados na tag [fields] existirem na tabela.
Mas se você quiser [atualizar] o registro se ele for encontrado, use a regra [insert_updateifexists]. Para esta regra, o update será realizado usando na cláusula [where] a [chave primária] da tabela.

Formato de Campo Data
Sempre que passar um valor de data, passar no seguinte formato:


yyyyMMddTHHmmssZ


Exemplo: 20230616T095600Z


Se precisar [atualizar] o campo no CRM com valor Nulo:
<field name='dtFieldName' type='datetime' value='NULL' />


Se precisar [atualizar] o campo no CRM com a data [corrente]:
<field name='dtFieldName' type='datetime' value='GETDATE' />


Formato de Campo Decimal
Sempre que passar um valor decimal, usar o formato americano, somente com o separador de centavos:


Ex: 1234.56



Para efetuar um UPDATE

1 - Monte o "UPDATE" através de uma instrução XML:
string xmlUPDATE = @"
    <update>
        <organization name='{0}' />
        <table name='{1}' setWorkflowTrigger='false' />
        <set>
            <field name='name' type='varchar' value='TEST UPD...' setWorkflowFieldTrigger='false' />
            <field name='modifiedOn' type='datetime' value='GETDATE' />
        </set>
        <filter type='and'>
            <condition field='name' operator='=' value='TEST' />
        </filter>
    </update>";

2 - Passe como parâmetro as informações de [Organização] e [tabela], conforme a sequência abaixo.
var xmlF = string.Format(xmlUPDATE, "CRMGT1", "Status");

3 - Execute a instrução, chamando a Web Api:
var header = new Dictionary<string, object>();
header.Add("Authorization", $"Bearer {token}");

var content = new Dictionary<string, object>();
content.Add("Type", "SET");
content.Add("Caller", "My Function Caller Name");
content.Add("xmlContent", xmlF);

var result = ExecutePostAsync("https://crm-gt.com/api/CRMGT/QueryXML", content, header);


Para efetuar um DELETE

1 - Monte o "DELETE" através de uma instrução XML:
string xmlDELETE = @"
    <delete>
        <organization name='{0}' />
        <table name='{1}' setWorkflowTrigger='false' />
        <filter type='and'>
            <condition field='name' operator='=' value='TEST...' />
        </filter>
    </delete>";

2 - Passe como parâmetro as informações de [Organização] e [tabela], conforme a sequência abaixo.
var xmlF = string.Format(xmlDELETE, "CRMGT1", "Status");

3 - Execute a instrução, chamado a Web Api:
var header = new Dictionary<string, object>();
header.Add("Authorization", $"Bearer {token}");

var content = new Dictionary<string, object>();
content.Add("Type", "SET");
content.Add("Caller", "My Function Caller Name");
content.Add("xmlContent", xmlF);

var result = ExecutePostAsync("https://crm-gt.com/api/CRMGT/QueryXML", content, header);


Para executar uma [QUERY EXEC]

1 - Monte a "QUERY EXEC" através de uma instrução XML:
string xmlQE = @"
	<queryexec>
	<organization name='{0}' />
	<queryexecid value='{1}' />
	<paramset>
		<param name='@_id_' type='uniqueidentifier' value='62a9a715-8574-4813-a69f-e732c76313e0' />
	</paramset>
	<filter type='and'>
		<condition field='inc.StatusCode' operator='=' value='1' />
	</filter>
	</queryexec>";

2 - Passe como parâmetro as informações de [Organização] e [ID da query exec], conforme a sequência abaixo.
var xmlF = string.Format(xmlQE, "CRMGT1", "buscarocorrencias");

3 - Execute a instrução, chamado a Web Api:
var header = new Dictionary<string, object>();
header.Add("Authorization", $"Bearer {token}");

var content = new Dictionary<string, object>();
content.Add("Type", "QueryExec");
content.Add("Caller", "My Function Caller Name");
content.Add("xmlContent", xmlF);

var result = ExecutePostAsync("https://crm-gt.com/api/CRMGT/QueryXML", content, header);

Obs: As tags <paramset> e <filter> são [opcionais]. Elas são úteis quando precisar passar mais parâmetros de filtro para a Query Exec criada no sistema ou para substituir o valor das macros do sistema (@_id_ por exemplo).

Nenhum comentário:

Postar um comentário

<< Ao enviar um comentário, favor clicar na opção [Enviar por e-mail comentários de acompanhamento para gtezini@gmail.com] >>