Careers Xpand IT Careers Xpand IT
Contact Us
marketing.talent@careers.xpand-it.com
Careers Xpand ITCareers Xpand IT
  • Procurar Oportunidades
    • Oportunidades de Emprego
    • Estudantes & Universidades
  • Life at Xpand IT
    • Trabalhar na Xpand IT
    • Os nossos Valores
  • Recursos
    • Blog
    • Webinares e Conteúdos
    • ITalks lives @Instagram
  • Quem Somos
    • Equipas Tecnológicas
    • Equipas Corporativas
    • O que fazemos
    • Imprensa
  • Contactos
  • Português
  • Inglês
Info
Junta-te a nósJunta-te a nósJunta-te a nós
Search website
Search
Close

Como aplicar deployments no Tableau Server através de uma abordagem DevOps?

  1. Home
  2. Blog Xtech Community
Como aplicar deployments no Tableau Server através de uma abordagem DevOps?
14-12-2021
Tempo de leitura: 9 minutos

Como aplicar deployments no Tableau Server através de uma abordagem DevOps?

por Andreia Barros, Business Intelligence Developer @ Xpand IT

Quando falamos do ciclo de vida do desenvolvimento de software, existem três grandes etapas pelas quais todos os programadores precisam de passar – Desenvolvimento, Staging e Implementação.

No Tableau, embora estejamos a falar de dashboards e bases de dados/fontes de dados, este processo é uma necessidade para assegurar a coisa mais importante quando falamos em dados – a qualidade dos dados.

Para implementar este processo, é necessário existirem três ambientes diferentes:

  • Desenvolvimento (onde o programador cria as fontes de dados e os workbooks)
  • Staging (onde as fontes de dados e os workbooks são implantados pelos programadores para serem testados pelos QA analysts)
  • Produção (Onde as fontes de dados e os workbooks são finalmente implantados para serem utilizados pelos utilizadores finais)

Isto pode ser facilmente implementado através do Tableau Server/Online com duas abordagens possíveis – múltiplas instâncias do Tableau Server ou uma instância com multi-tenancy (múltiplos sites no mesmo Tableau Server).

Então, qual é o problema principal?

Como podemos migrar o conteúdo de uma instância do Tableau para outra? Haverá uma forma automática? Ou será que o programador/QA analyst precisa de descarregar o conteúdo de um Servidor Tableau e carregá-lo para outro?

Qual é o objetivo?

O objetivo é criar um processo que faça esta migração de conteúdos de forma automática para que o utilizador não tenha de o fazer manualmente. Esta migração incluirá objetos, tais como fontes de dados, workbooks e projetos, e incluirá também as permissões atribuídas a cada um deles.

Existe também um segundo objetivo que é o de assegurar o controlo das versões destes objetos e permissões.

Casos de uso

Caso 1 – Criação/atualização de objetos

Um utilizador criou ou atualizou um objeto no ambiente de teste. Estas alterações têm de ser transpostas para o ambiente de produção.

Caso 2 – Mudança de permissões

Um utilizador alterou as permissões para um objeto no ambiente de teste. Estas alterações têm de ser transpostas para o ambiente de produção, para garantir a segurança dos objetos.

Caso 3 – Eliminar objetos

Um utilizador eliminou um objeto no ambiente de teste. Essa alteração deve ser transposta no ambiente de produção.

Assim, todas as alterações destes casos de uso têm de ser transpostas para um ambiente de produção.

Tipos de Migração

A migração de dados pode ser dividida em dois tipos:

  • Migração Completa – todos os objetos serão migrados, a máquina final será uma cópia da fonte (normalmente entre Teste → Produção)
  • Migração Parcial – existem filtros que dividem o conteúdo que está pronto para ser migrado do restante conteúdo. (Normalmente entre Desenvolvimento → Teste)

Esta separação é importante porque o método de migração a aplicar será diferente para cada um deles.

Tableau Server Dev Staging Prod
Fonte: Xpand IT

Na Migração Parcial, o criador do conteúdo terá a tarefa de criar uma pasta “Sandbox” no Tableau Server que incluirá todos os objetos aprovados e prontos para serem migrados. No final da migração, o ambiente de teste será uma cópia exata desta pasta.

Qual é a solução?

A solução estará dividida em três grandes etapas:

  1. Migração de objetos (download e upload)
  2. Migração de permissões
  3. Remoção de objetos

O processo tem de estar dividido nestas etapas porque a implementação de cada uma delas será distinta e dependerá de diferentes plataformas e métodos.

Como fazer a migração de objetos?

Solução – Usar o TabMigrate, uma ferramenta desenvolvida pela Tableau, para fazer o download e o upload dos objetos.

Tableau Server
Fonte: Xpand IT

Este processo terá cinco passos:

  1. Eliminar todo o conteúdo presente na pasta definida no TabMigrate para o download
  2. Descarregar os objetos, usando o comando TabMigrate “siteExport“
  3. Adicionar os objetos ao Git Repository
  4. Se estiveres a utilizar uma pasta diferente para carregar o conteúdo, envia o conteúdo do repositório para essa pasta
  5. Carregar os objetos para o Tableau Server, usando o comando do TabMigrate “siteImport“

TabMigrate Download

C:\Users\{User}\AppData\Local\Apps\2.0\NL4VADC2.ZZ7\MPVM8832.QG4\tabr..tion_0000000000000000_0001.0000_47e7c75dc9f35068\TabRESTMigrate.exe

    -command siteExport

    -fromSiteUserId [email protected]

    -fromSiteUserPassword *****

    -exportDirectory C:\path

    -fromSiteUrl https://dev.tableau.com/#/site/

    -fromSiteIsSystemAdmin true

    -backgroundKeepAlive false

    -logVerbose true

    -downloadInfoFiles true

    -exportOnlyTagged prod (opcional)

    -exportOnlyTaggedRemoveTag true (vem com o export tag, senão é opcional )

    -exportSingleProject Sandbox (opcional)

    -logFile C:\path\siteExport_log.txt

    -errorFile C:\path\siteExport_errors.txt

    -exitWhenDone true

Este comando permite que o utilizador defina as seguintes variáveis:

  • fromSiteUserId – Tableau Server Username
  • fromSiteUserPassword – Tableau Server Password
  • fromSiteUrl – Tableau Server Site Path, deixar “https://dev.tableau.com/#/” se for o predefinido
  • exportDirectory – Diretório local para onde o TabMigrate irá descarregar o conteúdo

Também permite ao utilizador definir uma pasta específica, usando o “exportSingleProject“. Ou uma etiqueta específica usando a variável “exportOnlyTagged“.

TabMigrate Upload

C:\Users\{User}\AppData\Local\Apps\2.0\NL4VADC2.ZZ7\MPVM8832.QG4\tabr..tion_0000000000000000_0001.0000_47e7c75dc9f35068\TabRESTMigrate.exe

    -command siteImport

    -toSiteUserId afpb

    -toSiteUserPassword *****

    -importDirectory C:\path

    -toSiteUrl https://dev.tableau.com/#/site/site

    -toSiteIsSiteAdmin true

    -remapDataserverReferences true

    -remapContentOwnership true

    -logVerbose true

    -dbCredentialsFile C:\path1\credentials.xml

    -logFile C:\path\siteImport_log.txt

    -errorFile C:\path\siteImport_errors.txt

    -manualStepsFile C:\path\siteImport_manualSteps.csv

    -exitWhenDone true

Este comando permite que o utilizador defina as seguintes variáveis:

  • toSiteUserId – Tableau Server Username
  • toSiteUserPassword – Tableau Server Password
  • toSiteUrl – Tableau Server Site Path, deixar “https://dev.tableau.com/#/” se for o predefinido
  • importDirectory – Directório local a partir do qual o TabMigrate irá ler o conteúdo a carregar

Ao carregar fontes de dados ou workbooks ligados a fontes de dados, é necessário adicionares um ficheiro XML com as credenciais (dbCredentialsFile).

<xml>

<credential contentType="workbook" contentProjectName="Test Site Import" contentName="test.twbx" dbUser="SimpleUser" dbPassword="q.123456" credentialIsEmbedded="true"> </credential>

<credential contentType="workbook" contentProjectName="Test Site Import" contentName="test2.twbx" dbUser="SimpleUser2" dbPassword="q.1234567" credentialIsEmbedded="false”> </credential>

<credential contentType="datasource" contentProjectName="Test Site Import" contentName="test2.tds" dbUser="SimpleUser3" dbPassword="q.12345678" credentialIsEmbedded="true"> </credential>

</xml>

Há algumas questões que deves ter em conta em relação às permissões:

  • Ao carregar um objeto, se o utilizador do objeto não existir no Tableau Server onde está a carregar, podes colocar a opção “remapContentContentContent” falso. E o TabMigrate vai deixar de tentar encontrar esse utilizador, e simplesmente fará do ToSiteUser o proprietário do conteúdo
  • Onde o objeto já exista no Tableau Server que está a carregar, o objeto permanece com as permissões já aí definidas

Se o objeto não existir no Tableau Server que está a carregar, o objeto irá assumir as permissões do seu projeto padrão.

Inventário do TabMigrate

C:\Users\User\AppData\Local\Apps\2.0\NL4VADC2.ZZ7\MPVM8832.QG4\tabr..tion_0000000000000000_0001.0000_47e7c75dc9f35068\TabRESTMigrate.exe

    -command siteInventory

    -fromSiteUserId [email protected]

    -fromSiteUserPassword *****

    -inventoryOutputFile C:\path\siteInventory.csv

    -fromSiteUrl https://dev.tableau.com/#/site/testesite

    -fromSiteIsSystemAdmin true

    -logVerbose true

    -logFile C:\path\siteExport_log.txt

    -errorFile C:\path\siteExport

    -exitWhenDone true

Este comando permite que o utilizador defina as seguintes variáveis:

  • fromSiteUserId – Tableau Server Username
  • fromSiteUserPassword – Tableau Server Password
  • fromSiteUrl – Tableau Server Site Path, deixar “https://dev.tableau.com/#/” se for o predefinido
  • inventoryOutputFile – Diretório local para o qual TabMigrate irá gerar e descarregar o inventário CSV

Se o utilizador for administrador, este pode mudar a flag “fromSiteIsSystemAdmin” para verdadeiro, e TabMigrate também irá gerar os utilizadores do site e informações sobre o site.

Migração de Permissões

Existem duas soluções principais para o caso das permissões:

  • O utilizador bloqueia as permissões do projeto no target e na máquina de origem, por isso quando os objetos são migrados da máquina de origem, as permissões que lhes estão aplicadas permanecem as mesmas que as definidas no Servidor Tableau de destino (manual/automático)
  • O utilizador não bloqueia as permissões, pelo que é necessário um processo que migre a permissão da fonte para a instância do Servidor Target Tableau (automático)

Permissões bloqueadas

O criador tem que definir as permissões para a pasta e depois bloqueá-las. Isto pode ser feito manualmente ou através do REST API.

Tableau Server Content permissions
Fonte: Xpand IT

Se decidires utilizar o REST API, terás de implementar dois passos:

  • Definir as permissões que gostarias de aplicar, e aplicá-las ao projeto principal (REST API – Add project permissions)
  • Atualizar o campo do projeto “contentpermissions” para o valor “LockedToProject“, bloqueando assim as permissões (REST API – Update Project)

Migração de Permissões (Automática)

Esta solução está localizada no REST API. Utilizaremos o REST API para recuperar as permissões da origem que pretendemos migrar e para depois inseri-las no Servidor Tableau de destino.

Atualmente, a REST API não tem uma operação padrão para permissões de ATUALIZAÇÃO. Para atualizar as permissões no Tableau Server de destino, precisamos primeiro de EXCLUIR as permissões que desejamos substituir e, em seguida, INSERIR as novas.

Tableau Server Rest Api
Fonte: Xpand IT

Exemplo (Migrar Permissões de Fontes de Dados)

1 – Tableau Server Sign in

O primeiro passo é entrar no Tableau Server para resgatar um token. Através do “username/password” ou “personal_access_token_name/personal_access_token_secret“.

Para encontrar o token de Acesso Pessoal (que é o mais seguro), precisas de entrar no Tableau Server como administrador, ir a “Account Settings” e depois criar um novo “Personal Access Token“.

Depois disso, podes facilmente entrar no Tableau Server usando o seguinte código python:

REST API + Python – Login

#Define server name, version and site (empty if default)server_name = "{domain}"

version = "3.9"

site_url_id = ""

personal_access_token_name = "token_name"

personal_access_token_secret = "token_secret"

signin_url = "https://{server}/api/{version}/auth/signin".format(server=server_name, version=version)

payload = { "credentials": { "personalAccessTokenName": personal_access_token_name, "personalAccessTokenSecret": personal_access_token_secret, "site": {"contentUrl": site_url_id }}}

headers = {

    'accept': 'application/json',

    'content-type': 'application/json'

}

#Rest API call to login

req = requests.post(signin_url, json=payload, headers=headers, verify=False)

req.raise_for_status()

#Keep the token into one variable (token)

token = response["credentials"]["token"]

site_id = response["credentials"]["site"]["id"]

headers['X-tableau-auth']=token

2 – Descarregar as permissões e guardá-las num CSV

Existem 4 grandes etapas a ter em conta:

  1. Encontrar o ID da fonte de dados
  2. Chamar o REST API para descarregar as permissões da fonte de dados (REST API – Query Datasource Permissions)
  3. Chamar o REST API irá recuperar apenas os IDs dos utilizadores/grupos, mas é necessário descobrir os seus nomes, para mapeá-los para o Servidor Tableau de destino
  4. Chamar o REST API para recuperar a informação sobre os utilizadores/grupos (REST API – Users and Groups)
  5. Descarregar esta informação para um CSV

REST API – Descarregar Permissões

# Get id by CSV

datasource_name = "example"

with open("C:path\\inventory.csv", "r") as filestream:

    for line in filestream:

        lines= line.split(",")

        if lines[3] == datasource_name:

            id = lines[2]

#Get permissions

permissions_url = "https://{server}/api/{version}/sites/{site_id}/datasources/{datasource_id}/permissions".format(server=server_name, version=version, site_id =site_id, datasource_id=id)

server_response = requests.get(url=permissions_url, data=None, headers=headers)

response1 = json.loads(server_response.content)

owner = response1["permissions"]["datasource"]

permissions = response1["permissions"]["granteeCapabilities"]

groups_of_permissions = response1["permissions"]["granteeCapabilities"]

#Transform the json into a list of permissions

a = 0

full_list = []

for group in groups_of_permissions:

    a = a + 1

    list = []

    for item in group.keys():

        if item == "group" :

            type1 = (group["group"]["id"])

            typedsc=item

        elif item == "user":

            type1 = (group["user"]["id"])

            typedsc=item

        else :

            j = []

            for type in group["capabilities"]["capability"]:

                j.append((type["name"],type["mode"]))

            list.append((type1, typedsc,j))

    full_list.append(list)

#Retrieve the name of the users/groups

users_url = "https://{server}/api/{version}/sites/{site_id}/users/".format(server=server_name, version=version, site_id=site_id)

groups_url = "https://{server}/api/{version}/sites/{site_id}/groups/".format(server=server_name, version=version, site_id=site_id)

users_response = requests.get(url=users_url, data=None, headers=headers)

users = json.loads(users_response.content)["users"]["user"]

groups_response = requests.get(url=groups_url, data=None, headers=headers)

groups1 = json.loads(groups_response.content)["groups"]["group"]

#Create final list and save into a CSV (datasource_id, datasource_name, type, user_id, username, permission_name, permission_mode)

final_list = []

for x in full_list :

    user_id=x[0][0]

    type=x[0][1]

    if type == "group":

        for group in (group for group in groups1 if group["id"]==user_id):

            uname = group["name"]

    else:

        for user in (user for user in users if user["id"]==user_id):

            uname = user["name"]

    for (name,mode) in x[0][2]:

        final_list.append([id,datasource_name, type, user_id, uname, name, mode])

 with open('C:\\path\\permissions_origin.csv', mode='w', newline='') as csv1:

    writer = csv.writer(csv1)

    writer.writerows(final_list)

Neste momento, tem um CSV com todas as permissões. Isto tem de ser executado tanto para a origem como para o Servidor Tableau de destino:

  • csv (origem)
  • csv (target)

3 – Eliminar as permissões existentes no target

Uma vez que já temos um CSV, todos os dados sobre as permissões que serão apagadas, esta tarefa torna-se muito fácil. Seguindo estes passos:

  • Obter as permissões para apagar
  • Chamar o REST API correto para apagar (REST API – Delete Datasource Permissions)

REST API + Python – Eliminar Permissões

#Get the permissions

with open('C:\\{path_to_file}\\permissions_origin.csv', 'r') as permissions:

    reader = csv.reader(permissions)

    permission = list(reader)

#Delete

for perm in permission:

    if perm[2] == "group":

        deleteURL = "https://{server}/api/{version}/sites/{site_id}/datasources/{datasource_id}/permissions/groups/{group_id}/{cap_name}/{cap_mode}" \

            .format(server=server_name, version=version, site_id=site_id, datasource_id=perm[0], group_id=perm[3],

                    cap_name=perm[5], cap_mode=perm[6])

        req = requests.delete(url=deleteURL, json=payload, headers=headers, verify=False)

    else:

        deleteURL = "https://{server}/api/{version}/sites/{site_id}/datasources/{datasource_id}/permissions/users/{user_id}/{cap_name}/{cap_mode}" \

            .format(server=server_name, version=version, site_id=site_id, datasource_id=perm[0], user_id=perm[3],

                    cap_name=perm[5], cap_mode=perm[6])

4 – Inserir novas permissões no target

As permissões que serão inseridas já estão no ficheiro “permissions_staging.csv”, mas estão com o antigo ID, pelo que é preciso mapeá-las para o ID correto (target ID)

Assim, as etapas serão as seguintes:

  • Obter as permissões para inserir
  • Mapeá-las para o novo ID
  • Criar um XML com as permissões para adicionar
  • Chamar o REST API para as inserir (REST API – Add datasource permissions)

REST API + Python – Adicionar Permissões

#Get the users and groups ids

users_url = "https://{server}/api/{version}/sites/{site_id}/users/".format(server=server_name, version=version, site_id=site_id)

groups_url = "https://{server}/api/{version}/sites/{site_id}/groups/".format(server=server_name, version=version, site_id=site_id)

users_response = requests.get(url=users_url, data=None, headers=headers)

users = json.loads(users_response.content)["users"]["user"]

groups_response = requests.get(url=groups_url, data=None, headers=headers)

groups1 = json.loads(groups_response.content)["groups"]["group"]

inventory_lines = []

with open("C:\\path\\inventory.csv", "r") as filestream:

    for line in filestream:

        inventory_lines.append(line.split(","))

#Get the permissions to add and map them to the correct id

with open('C:\\path\\permissions_origin.csv', 'r') as permissions:

    reader = csv.reader(permissions)

    permission = list(reader)

permissions_ids = []

for perm in permission:

    if perm[2] == "group":

        for group in (group for group in groups1 if group["name"] == perm[4]):

            uid = group["id"]

    else:

        for user in (user for user in users if user["name"] == perm[4]):

            uid = user["id"]

    for lines in (lines for lines in inventory_lines if lines[3] == perm[1]):

        datasource_id = lines[2]

    permissions_ids.append([perm[2],datasource_id,uid,perm[5],perm[6]])

#Generate the XML and call the REST API

headers = {

    'X-tableau-auth': token,
}

for perm in permissions_ids:

    if perm[0] == "user":

        request_xml = ET.Element('tsRequest')

        permissions_xml = ET.SubElement(request_xml, 'permissions')

        datasource_xml = ET.SubElement(permissions_xml, 'datasource', id=perm[1])

        grantee = ET.SubElement(permissions_xml, 'granteeCapabilities')

        user_xml = ET.SubElement(grantee, 'user', id=perm[2])

        capabilities_xml = ET.SubElement(grantee, 'capabilities')

        capability_xml = ET.SubElement(capabilities_xml, 'capability', name=perm[3], mode=perm[4])

        token_url = "https://{server}/api/{version}/sites/{site_id}/datasources/{ds}/permissions" \

            .format(server=server_name, version=version, site_id=site_id, ds=perm[1])

        request_data = ET.tostring(request_xml)

        x = requests.put(url=token_url, data=request_data,headers=headers)

    else:

        request_xml = ET.Element('tsRequest')

        permissions_xml = ET.SubElement(request_xml, 'permissions')

        datasource_xml = ET.SubElement(permissions_xml, 'datasource', id=perm[1])

        grantee = ET.SubElement(permissions_xml, 'granteeCapabilities')

        group_xml = ET.SubElement(grantee, 'group', id=perm[2])

        capabilities_xml = ET.SubElement(grantee, 'capabilities')

        capability_xml = ET.SubElement(capabilities_xml, 'capability', name=perm[3], mode=perm[4])

        token_url = "https://{server}/api/{version}/sites/{site_id}/datasources/{ds}/permissions" \

            .format(server=server_name, version=version, site_id=site_id, ds=perm[1])

        request_data = ET.tostring(request_xml)

        x = requests.put(url=token_url, data=request_data, headers=headers)

Eliminar Artefactos

O último passo é descobrir se existe algum ficheiro que tenha sido eliminado no Origin Tableau Server, para eliminar o mesmo ficheiro no Target Tableau Server.

A tarefa fundamental nesta etapa é descobrir que objetos foram eliminados.

Dependendo do tipo de migração, podemos utilizar uma das duas soluções seguintes:

  1. Descobrir o que foi eliminado através da diferença entre os dois últimos Git commits (aplicável à Migração Parcial e à Migração Completa)
  2. Descobrir o que foi eliminado através da diferença entre o inventário das duas máquinas Tableau Server (aplicável apenas à Migração Completa)

A segunda solução é mais fácil de implementar, por isso, se só utilizas a Migração Completa na tua empresa, aconselhamos a que escolhas essa solução. Se utilizares alguma Migração Parcial, terás de escolher a primeira.

Git difference

Git differences
Fonte: Xpand IT

Existem três passos principais se escolher esta abordagem:

  1. Descobrir o que foi eliminado, através do Gif Diff
  2. Descobrir o tipo de objeto
    1. Workbooks (.twb ou .twbx)
    2. Fonte de dados (.tds ou .tdsx)
    3. Projetos (no extension)
  3. Chamar o REST API apropriado para apagar o objeto

Diferença de Inventário

Existem quatro etapas se escolher esta abordagem:

  1. Descarregar o Origin Tableau Server Inventory
  2. Descarregar o Target Tableau Server Inventory
  3. Descobrir o que é que foi eliminado através da diferença entre eles
  4. Chamar o REST API apropriado para apagar o objeto

Conclusão

Existem algumas áreas que este modelo não cobre, tais como:

  • Migração de um extrato com um tamanho maior do que o limite do ficheiro Git
  • Cronograma dos extratos/workbooks
  • Quando fazemos a migração de um extrato ou workbook usando o TabMigrate, o cronograma é perdido aquando da migração

Uma solução possível para a migração de extratos maiores é:

  1. Descarregar a fonte de dados sem extrair
  2. Carregar a fonte de dados, sem substituir a existente
  3. Chamar o REST API para extrair a fonte de dados
  4. Quando a extração estiver terminada, substituir a antiga fonte de dados pela nova
Categories:
  • Blog Xtech Community
Tags:
  • DevOps
  • Tableau Server
Anterior Seguinte
  • Popular
  • Recente
Três dicas para criar um CV de engenharia informática de sucesso
Mai 08, 20

Três dicas para criar um CV de engenharia informática de sucesso

Como ser um bom developer? Não é apenas sobre código
Fev 03, 20

Como ser um bom developer? Não é apenas sobre código

A minha primeira experiência profissional nos estágios de verão da Xpand IT
Set 20, 19

A minha primeira experiência profissional nos estágios de verão da Xpand IT

Como começar a trabalhar em IT? – live instagram
Abr 27, 21

Como começar a trabalhar em IT? – live instagram

Sete conselhos que um junior developer deve saber
Set 16, 19

Sete conselhos que um junior developer deve saber

Teletrabalho: boas práticas para trabalhar em casa (miúdos incluídos)
Mar 31, 20

Teletrabalho: boas práticas para trabalhar em casa (miúdos incluídos)

Cinco razões para continuar a trabalhar na mesma empresa de TI
Jan 22, 20

Cinco razões para continuar a trabalhar na mesma empresa de TI

Data Engineer ou Data Scientist? Descobre a carreira que faz match contigo
Nov 09, 20

Data Engineer ou Data Scientist? Descobre a carreira que faz match contigo

Metodologias ágeis: cinco lições a partir do coaching a diferentes equipas
Dez 26, 19

Metodologias ágeis: cinco lições a partir do coaching a diferentes equipas

Comecei um novo desafio profissional em teletrabalho. Foi assim a minha experiência
Mai 14, 20

Comecei um novo desafio profissional em teletrabalho. Foi assim a minha experiência

De Trainee a Partner: Como chegar a este nível e quais os desafios?
Jun 23, 22

De Trainee a Partner: Como chegar a este nível e quais os desafios?

10 hacks em Javascript que os developers devem conhecer
Mai 17, 22

10 hacks em Javascript que os developers devem conhecer

10 top skills procuradas num Senior Developer
Abr 21, 22

10 top skills procuradas num Senior Developer

A importância de UX e as suas aplicações no futuro – live instagram
Abr 18, 22

A importância de UX e as suas aplicações no futuro – live instagram

Angular vs React: qual escolher? Conhece aplicações práticas
Mar 14, 22

Angular vs React: qual escolher? Conhece aplicações práticas

5 razões para trabalhares numa consultora de IT
Fev 09, 22

5 razões para trabalhares numa consultora de IT

De Native para Flutter: a opinião de um mobile developer
Jan 07, 22

De Native para Flutter: a opinião de um mobile developer

Termos e Condições de Participação no Passatempo Auscultadores Bluetooth
Dez 20, 21

Termos e Condições de Participação no Passatempo Auscultadores Bluetooth

Como aplicar deployments no Tableau Server através de uma abordagem DevOps?
Dez 14, 21

Como aplicar deployments no Tableau Server através de uma abordagem DevOps?

4 lições que aprendi sobre liderança ao correr uma meia-maratona
Nov 17, 21

4 lições que aprendi sobre liderança ao correr uma meia-maratona

pin

Portugal

pin

United Kingdom

pin

Sweden

Carreira em IT logo Xpand IT
Subscrever Newsletter
  • Procurar Oportunidades
  • Oportunidades de Emprego
  • Estudantes & Universidades
  • Life at Xpand IT
  • Trabalhar na Xpand IT
  • Os nossos valores
  • Recursos
  • Blog
  • Webinares e Conteúdos
  • ITalks lives @Instagram
  • Quem Somos
  • Equipas de Produto & Tecnologia
  • Equipas Corporativas
  • O que fazemos
  • Imprensa
  • Contactos
www.xpand-it.com  2022 | All rights reserved
Legal Privacy Policy Terms Of Use
Lisboa 2020 Logo norte 2020
logo Compete 2020
Logo Portugal 2020
Logos CMMI e Pledge 1


X
This website uses Cookies
We use cookies on our website to give you the most relevant experience by remembering your preferences and repeat visits. By clicking “Accept All”, you consent to the use of all the cookies. However, you may visit Cookie Settings to provide a controlled consent.
By browsing our website, you agree to ourCookie Policy
Cookie settingsACCEPT ALL
Manage consent

Privacy Overview

This website uses cookies to improve your experience while you navigate through the website. Out of these cookies, the cookies that are categorized as necessary are stored on your browser as they are essential for the working of basic functionalities of the website. We also use third-party cookies that help us analyze and understand how you use this website. These cookies will be stored in your browser only with your consent. You also have the option to opt-out of these cookies. But opting out of some of these cookies may have an effect on your browsing experience.
Necessários
Relatório de Consentimento
Os cookies necessários são essenciais para o nosso website funcionar normalmente. Assegurando anonimanente as funcionalidades básicas
CookieDuraçãoDescrição
__rd_experiment_versionsessãoEste cookie é configurado pelo RD Station garante que o utilizador veja sempre a mesma versão de um teste A/B, preservando a experiência e consistência dos dados.
_GRECAPTCHA5 meses 27 diasEste cookie é configuirado pelo Google reCAPTCHA protege o nosso site contra ataques de spam nos formulários de contacto.
cookielawinfo-checkbox-[CATEGORY]1 anoEste cookie é usado pelo plugin GDPR Cookie Consent guarda o consentimento do visitante da [CATEGORIA].
viewed_cookie_policy1 anoEste cookie usado pelo GDPR Cookie Consent plugin para guardar se o visitante consentiu ou não o use the cookies. Não guarda nenhum dado pessoal.
Performance e Analíticos
Estes cookies são usados em análises estatísticas, permitem-nos analisar o comportamente de navegação no nosso site de forma a melhorar os serviços e campanhas que disponibilizamos.
CookieDuraçãoDescrição
__trf.src1 anoEste cookie é configurado pelo RD Station, guarda a referência da origem da visita ao site.
_fbp3 mesesEste cookie é configurado pelo Facebook para exibir anúncios quando estiver no Facebook ou noutra plataforma digital onde é usada publicidade do Facebook
_ga2 anosEste cookie é configurado pelo Google Analytics, calcula dados de visitantes, sessões, campanhas e acompanha o uso do site. O cookie armazena informações anónimas atribuindo um número aleatório para distinguir visitantes únicos.
_ga_[ID]2 anosEste cookie é configurado pelo Google Analytics é usado para distinguir os visitantes.
_gat_UA-[ID]1 minutoEste cookie é configurado pelo Google Analytics, onde o elemento padrão no nome contém um número que identifica o site. É uma variação do cookie _gat usado para limitar a quantidade de dados registados pelo Google em sites de alto volume de tráfego.
_gid1 diaEste cookie é configurado pelo Google Analytics guarda um ID único que é usado para gerar dados estatísticos sobre como o visitante usa o site
_rdtrk9 anos 8 meses 4 dias 9 horas 21 minutosEste cookie é configurado pelo RD Station, guarda a lista de todas as páginas que o visitante acedeu.
AnalyticsSyncHistory1 mêsEste cookie é usado pelo Linkedin para guardar informação sobre quando ocorreu a sincronização com o cookie lms_analytics para os visitantes dos países selecionados.
bcookie2 anosEste cookie é usado pelo Linkedin para guardar informação sobre quando ocorreu a sincronização com o cookie lms_analytics para os visitantes dos países selecionados.
fr3 mesesEste cookie é usado pelo Facebook permite mostrar anúncios relevantes aos visitantes, analisando o comportamento do visitante noutros websites que possuem pixel do Facebook ou o plugin social do Facebook.
rdtrk1 anoEste cookie é configurado pelo RD Station, guarda a lista de todas as páginas que o visitante acedeu.
UserMatchHistory1 mêsEste cookie usado pelo LinkedIn para sincronizar os ID's dos Ads.
YSCsessãoEste cookie é usado pelo Youtube para guardar as visualizações de videos do youtube embebidos em páginas.
yt-remote-connected-devicesnuncaEste cookie é usado pelo YouTube para guardar as preferências de video do visitante nos videos embebidos.
yt-remote-device-idnuncaEste cookie é usado pelo YouTube para guardar as preferências de video do visitante nos videos embebidos.
yt.innertube::nextIdnuncaEste cookie é usado pelo YouTube regista um ID único para guardar quais os vídeos que o visitante visualizou.
yt.innertube::requestsnuncaUsed by YouTube, registers a unique ID to store data on what videos from YouTube the user has seen.
Outros
Cookies que estão atualmente a ser analisados pela nossa equipa.
CookieDuraçãoDescrição
cookietestsessãoEste cookie está a ser analisado pela nossa equipa.
cxssh_status3 meses 8 diasEste cookie está a ser analisado pela nossa equipa.
Funcionais
Estes cookies são usadas para melhorar a experiência e funcionalidades do nosso website, permitem também guardar as preferências do utlizador para futuras vistas.
CookieDuraçãoDescrição
_icl_visitor_lang_js1 diaEste cookie é configurado pelo WPML WordPress plugin guarda o idioma redirecionado.
bscookie2 anosEste cookie é usado pelo LinkedIn guarda se o utlizador fez login com multi-factor
CONSENT2 anosEste coookie é usado pelo YouTube em videos embebidos e regista dados estatísticos anonimamente.
langsessãoEste cookie é usado pelo LinkedIn para guardar a escolha do idioma do visitante no website linkedin.com.
li_gc2 anosEste cookie é usado pelo Linkedin para guardar o consentimento dos visitantes acerca do uso de cookies não essenciais.
lidc1 diaEste cookie é usado pelo LinkedIn para facilitar a escolha do datacenter.
VISITOR_INFO1_LIVE5 meses 27 diasEste cookie usado pelo YouTube para medir a largura de banda de modo a determinar se o visitante acede à nova ou à antiga interface.
wpml_browser_redirect_testsessãoEste cookie é usado pelo WPML WordPress plugin é usado para testar se os cookies estão ativos no browser.
Salvar e Aceitar
  • Português
  • Inglês