Pubblicato il 25/11/2023 da alnao nella categoria AWS

Il Cloud AWS mette a disposizione una serie di servizi per la gestione di base di dati di tipo Serverless, quindi il programmatore non deve preoccuparsi di gestire il server fisico/virtuale dove sono gestiti i dati ma deve solo preoccuparsi dei dati stessi. Il principale servizio di base dati DynamoDB è un servizio proprietario di AWS di tipo noSQL e schema-less quindi non si tratta di un database relazione e non si possono eseguire query nel linguaggio SQL ma sono presenti delle API che permettono di scrivere e leggere i dati. Si tratta di un database di tipo chiave-valore quindi bisogna sempre ricordare che ogni elemento deve sempre avere una chiave univoca che identifica un elemento, i dati contenuti negli elementi sono sempre rappresentati nel formato json standard e le chiavi univoche possono gestire il tipo HASH nativamente.

Il servizio è studiato per gestire carichi di lavoro di qualsiasi tipo, può gestire da poche richieste a più di 10 trilioni di richieste al giorno; infatti la disponibilità, la durabilità e tolleranza ai guasti sono funzionalità integrate che eliminano la necessità di progettare le applicazioni per queste funzionalità. Il costo del servizio è calcolato in base alla quantità di dati e al numero di richieste con 25 GB di archiviazione e fino a 200 milioni di richieste di lettura/scrittura al mese con il Piano gratuito AWS. Si rimanda sempre alla documentazione ufficiale per verificare costi e caratteristiche di DynamoDB.

Il servizio prevede molti sotto-servizi come DynamoDB stream, editor PartiQL, gestione di Clusters e molto altro ma bisogna sempre ricordare che l’entità base di questo servizio sono le tabelle che necessitano “sempre” una partition-key e una sort-key, campi necessari per permettere al servizio di gestire correttamente ai dati.

Nella console è possibile le tabella con una intuitiva console amministrativa:

Dove è possibile anche visualizzare e modificare gli item manualmente:


Con la CLI è possibile gestire tabelle in maniera molto semplice, infatti sono disponibili comandi per tutte le operazioni necessarie sulla base di dati ben descritti nella documentazione ufficiali, i principali comandi sono:

  • elenco delle tabelle:
    aws dynamodb list-tables
  • creazione di una tabella:
    aws dynamodb create-table --table-name nome-tabella --attribute-definitions AttributeName=id,AttributeType=S --key-schema AttributeName=id,KeyType=HASH --table-class STANDARD --provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5
  • scrittura di un elemento in una tabella:
    aws dynamodb put-item --table-name nome-tabella --item '{"id": {"S": "1"}, "Name": {"S": "Alberto"}}'
  • recupero di un elemento da una tabella:
    aws dynamodb get-item --consistent-read --table-name nome-tabella --key '{ "id": {"S": "1"}}'
  • operazione di lettura di tutti i dati di una tabella:
    aws dynamodb scan --table-name nome-tabella
  • operazione di lettura di tutti i dati di una tabella filtrando i campi recuperati:
    aws dynamodb scan --table-name nome-tabella --query "Items[*].[id.S,Name.S]"
  • scarico di tutti gli elementi di una tabella in formato json in un file:
    aws dynamodb scan --table-name nome-tabella --select ALL_ATTRIBUTES --output json > a.json
  • eliminazione di una tabella:
    aws dynamodb delete-table --table-name nome-tabella

Bisogna sempre ricordare che il comando che cancella una tabella esegue anche la cancellazione dei dati contenuti, non essendoci nessun controllo sulla presenta di dati in una tabella, bisogna sempre prestare attenzione quando si lancia questo comando per evitare che i dati vengano cancellati per sbaglio.


La libreria SDK boto3 permette di gestire da codice le tabelle in maniera veloce e semplice, la documentazione ufficiale è veramente ricca di esempi, i principali metodi sono:

def table_list(profile_name):
  boto3.setup_default_session(profile_name=profile_name)
  client = boto3.client('dynamodb')
  response = client.list_tables( Limit=100)
  tables = []
  for table in response['TableNames']:
    tables.append(table)
  return tables
def write_element_with_id(profile_name, table,element):
  boto3.setup_default_session(profile_name=profile_name)
  client = boto3.client('dynamodb')
  if 'id' not in element:
    element['id'] = str(uuid.uuid4()) #calcolo id univoco
  ts= TypeSerializer()
  serialized_post= ts.serialize(element)["M"]
  res = client.put_item(TableName=table,Item=serialized_post)
  res['id']=element['id']
  return res
def get_element_by_id(profile_name,table,id):
  boto3.setup_default_session(profile_name=profile_name)
  client = boto3.client('dynamodb')
  response = client.get_item( Key={ 'id': {'S': id, } } , TableName=table,)
  return response['Item']
def delete_element_by_id(profile_name,table,id):
  boto3.setup_default_session(profile_name=profile_name)
  client = boto3.client('dynamodb')
  response = client.delete_item( Key={ 'id': {'S': id, } } , TableName=table,)
  return response
def scan_table(profile_name,table):
  boto3.setup_default_session(profile_name=profile_name)
  client = boto3.client('dynamodb')
  response = client.scan(TableName=table,Limit=123)
  return response['Items']
def full_scan_table(profile_name,table):
  boto3.setup_default_session(profile_name=profile_name)
  dynamodb = boto3.resource('dynamodb')#, region_name="eu-west-1"
  table = dynamodb.Table(table)
  today = datetime.datetime.now()
  response = table.scan( ) #FilterExpression=Attr('country').eq('US') & Attr('city').eq('NYC')
  data = response['Items']
  while 'LastEvaluatedKey' in response:
    response = table.scan(ExclusiveStartKey=response['LastEvaluatedKey'], ) #FilterExpression
    data.extend(response['Items'])
  return data

Come si può notare per eseguire la ricerca si usa il metodo scan che prevede un sistema paginante con il parametro LastEvaluatedKey che permette le ricerche multiple, inoltre nelle esecuzioni del comando scan è possibile aggiungere il parametro FilterExpression se necessario aggiungere filtri su attributi non chiave, nell’esempio qui presente si usa un attributi id come chiave primaria ma è possibile costruire chiavi con chiavi primarie più complesse.


Con CloudFormation è possibile creare tabelle in maniera veloce e velocissima, il tipo specifico è veramente basico e prevede come parametri il nome, la definizione della chiave primaria e i parametri del “Throughput”.

Dynamo:
  Type: AWS::DynamoDB::Table
  Properties:
    TableName: !Ref DynamoName
    AttributeDefinitions:
      -
        AttributeName: "id"
        AttributeType: "S"
    KeySchema:
      -
        AttributeName: "id"
        KeyType: "HASH"
    ProvisionedThroughput:
      ReadCapacityUnits: "5"
      WriteCapacityUnits: "5"

L’esempio completo e funzionante è disponibile al solito repository

https://github.com/alnao/AWSCloudFormationExamples/tree/master/Esempio11dynamoApiCrud

Ovviamente questo servizio è studiato per interagire con altri servizi di tipo Serverless di AWS come Lambda, SQS, Step function e Glue.

In questo esempio è presente una lambda che scrive e legge i valori dalla tabella, in successivi esempi saranno introdotte anche altre operazioni con altri servizi.

Pubblicato il 25/11/2023 da alnao nella categoria Angular & Ionic

Il framework Angular mette a disposizione alcune tecniche per la gestione dei dati nei template, si tratta di tecniche per la gestione della visualizzazione dei dati ma anche tecniche per la gestione dei dati inseriti dall’utente senza l’uso dei form. In questo articolo saranno comprese le tecniche molto usate che non prevedono l’uso dei form e delle variabili, tecniche già esposte in precedenti articoli.

La tecnica più semplice è l’uso del carattere  detto anche Pipe (in inglese traduzione della parola tubo), questa tecnica viene descritta nel sito ufficiale e prevede la definizione di semplici espressioni che accettano un valore in input e ritornano il valore trasformato, sono molto usate perché possono essere usate nei template per trasformare e formattare i dati senza dover modificare il valore originale, un esempio semplice di utilizzo è la formattazione delle date che spesso nei servizi/webService sono nel formato standard json/javascript ma nei template è necessario visualizzarlo nel formato dd/mm/yyyy o simili.

{{valueDate | date: 'dd/MM/yyyy'}}

Le più comuni espressioni pipe previste dal framework sono: DatePipe, UpperCasePipe, LowerCasePipe, CurrencyPipe, DecimalPipe, PercentPipe.

E’ possibile scrivere espressioni personalizzate, per esempio per definire una espressione per eseguire l’esponenziale bisogna definire una classe che implementi il metodo di trasformazione:

import { Pipe, PipeTransform } from '@angular/core';
@Pipe({name: 'exponentialStrength'})
export class ExponentialStrengthPipe implements PipeTransform {
  transform(value: number, exponent = 1): number {
    return Math.pow(value, exponent);
  }
}

E poi è possibile usare l’espressione nei template

{{2 | exponentialStrength: 10}}

La documentazione ufficiale mette a disposizione diversi esempi semplici e spiegazioni complete riguardo a questo argomento. L’esempio completo dell’esponenziale infatti è completato nella documentazione ufficiale con l’uso delle variabili è l’uso del ngModel:

@Component({
  selector: 'app-power-boost-calculator',
  template: `
    <h2>Power Boost Calculator</h2>
    <label for="power-input">Normal power: </label>
    <input id="power-input" type="text" [(ngModel)]="power">
    <label for="boost-input">Boost factor: </label>
    <input id="boost-input" type="text" [(ngModel)]="factor">
    <p>
      Super Hero Power: {{power | exponentialStrength: factor}}
    </p>
  `,
  styles: ['input {margin: .5rem 0;}']
})
export class PowerBoostCalculatorComponent {
  power = 5;
  factor = 1;
}

Un’altra applicazione delle pipe è realizzare filtri sugli elenchi nei cicli ngFor, per esempio per filtrare i primi 50 elementi di una lista si può usare la espressione standard slice:

<li *ngFor="let elemento of listaCompleta | slice:0:50 ; let i = index;">
...
</li>

Ma è possibile usare anche slice personalizzare definire da classi specifiche, per esempio per eseguire un filter su una lista si può definre una espressione personalizzata:

@Pipe({ name: 'flyingHeroes' })
export class FlyingHeroesPipe implements PipeTransform {
  transform(allHeroes: Hero[]) {
    return allHeroes.filter(hero => hero.canFly);
  }
}

E nel template si può richiamare la espressione

<li *ngFor="let hero of (heroes | flyingHeroes)">
  {{hero.name}}
</li>

Questa tecnica è usata moltissimo dai programmatori anche per gestire gli oggetti json: in fase di sviluppo è necessario trasformare oggetti json in stringa per visualizzare l’intero oggetto in un template, nativamente Angular non visualizza gli oggetti complessi mentre l’uso del pipe json permette di avere a video la forma completa di un oggetto, la documentazione ufficiale spiega che è usato per convertire un valore nel suo formato json rappresentabile in pagina. L’esempio semplice di questo componente:

@Component({
  selector: 'json-pipe',
  template: `<div>
    <p>Without JSON pipe:</p><pre>{{object}}</pre>
    <p>With JSON pipe:</p><pre>{{object | json}}</pre>
  </div>`
})
export class JsonPipeComponent {
  object: Object = {foo: 'bar', baz: 'qux', nested: 
    {xyz: 3, numbers: [1, 2, 3, 4, 5]}};
}

Il framework mette a disposizione anche il pipe di tipo AsyncPipe che verrà introdotto nell’articolo specifico che palerà della gestione dei servizi e delle funzioni asincrone.

Pubblicato il 25/11/2023 da alnao nella categoria Wordpress

Gli aggiornamenti di WordPress sono un tema importante da non sottovalutare: è sempre conveniente mantenere aggiornato il framework e tutti i plugin per ragioni di sicurezza. Infatti WordPress è un framework gratuito e anche open-source, ciò significa che tutti i programmatori del mondo possono scaricare il codice sorgente e analizzarlo, purtroppo anche i mal-intenzionati hanno questa possibilità e appena si scopre una vulnerabilità c’è l’altissimo rischio che venga usata, gli aggiornamenti il principale obbiettivo di risolvere i problemi di sicurezza quando vengono scoperti. In particolare, i plugin sono la parte di WordPress più soggetta a vulnerabilità e nemmeno quelli più popolari e seguiti da team di sviluppatori forti ne sono immuni, trattandosi di codice scritto da terzi esternamente hanno aggiornamenti meno frequenti.

Nella console web è presente una pagina dedicata agli aggiornamenti dove vengono visualizzate tutte le versioni del framework e dei plugin con la possibilità di procedere con gli aggiornamenti di tutti gli aggiornamenti con un click:

Il framework principale, detto spesso core, ha un aggiornamento separato visto che necessità di più tempo

da notare che durante gli aggiornamenti il sito viene disattivato e posto in uno stato di “manutenzione”, infatti viene creato un file .maintenance che disattiva temporaneamente il sito, la modalità viene disattivata quando tutti gli aggiornamenti sono completati correttamente.

Oltre alla modalità manuale esiste una configurazione che permette di eseguire automaticamente gli aggiornamenti, delegando al framework l’ingrato compito di aggiornarsi scaricando gli aggiornamenti dal sito ufficiale in automatico, la guida ufficiale descrive i passi da compiere, questa modalità è consigliata per chi ha poca dimestichezza con il framework ma è sconsigliata per gli utenti esperto o i siti molto grossi che possono avere problemi causati dagli aggiornamenti, infatti è necessario concludere dicendo che non sempre gli aggiornamenti funzionano perfettamente e può capire che il comportamento del sito e il suo aspetto subisca variazioni indesiderate. In particolare è consigliato aggiornare una versione quando tutti i plugin dispongono aggiornamenti compatibili, in quanto può capitare che un plugin non venga aggiornato e che possa smettere di funzionare o creare problemi dopo l’aggiornamento del core del framework.

Pubblicato il 18/11/2023 da alnao nella categoria AWS

CloudFormation mette a disposizione una serie di tipi non atomici, cioè non corrispondenti ad un singolo servizio, ma che permettono di costruire applicazioni o infrastrutture che comprendono più elementi, l’esempio più evidente è la gestione dell’API Gateway che necessità di diversi elementi (stage, risorse, deploy) ma che può essere raggruppato in un unica risorsa nel template di CloudFormation.

I tipo messi a disposizione sono:

  • AWS::Serverless::Api
  • AWS::Serverless::Application
  • AWS::Serverless::Connector
  • AWS::Serverless::Function
  • AWS::Serverless::GraphQLApi
  • AWS::Serverless::HttpApi
  • AWS::Serverless::LayerVersion
  • AWS::Serverless::SimpleTable
  • AWS::Serverless::StateMachine

ben descritti nella documentazione ufficiale. In realtà questi tipo sono stati usato anche in precedenti esempi senza dar particolare attenzione alla differenza tra i tipi atomici e i tipi “serverless”, questo perché spesso risultano più semplici da imparare ed usare questi tipi rispetto ai tipi elementari.


Il primo tipo messo a disposizione dal framwork è studiato per creare API di tipo RESTful, questo permette di creare tutta l’infrastuttura (risorse, metodi e stage) in un unico componente indicando lo stage come parametro, i metodi in un elenco specifico e le risorse come funzioni Lambda esterne, infatti questo tipo di risorsa funziona solo se combinato con il tipo AWS::Serverless::Function. Un esempio completo e funzionante:

ApiGateway:
  Type: AWS::Serverless::Api
  Properties:
    StageName: !Ref Stage
    OpenApiVersion: 3.0.2
    CacheClusterEnabled: false
    CacheClusterSize: '0.5'
    Cors:
      AllowMethods: "'POST, GET, DELETE, OPTIONS'"
      AllowOrigins: "'*'"
      AllowHeaders: "'*'"
      MaxAge: "'600'"
    MethodSettings:
    - ResourcePath: /
      HttpMethod: GET
      CachingEnabled: false
      CacheTtlInSeconds: 300
ApiGetFunction: 
  Type: AWS::Serverless::Function
  Properties:
    Role: !GetAtt APIRole.Arn
    Events:
      ApiEvent:
        Type: Api
        Properties:
          Path: /
          Method: get
          RestApiId:
            Ref: ApiGateway
    CodeUri: lambda
    Handler: dynamo_crud.get_handler
    Runtime: python3.8
    MemorySize: 128
    Environment: 
      Variables:
        DynamoName: !Ref DynamoName
    Timeout: 500

L’esempio completo e funzionante di questo esempio (con API che esegue una chiamata ad una Lambda) è disponibile al solito repository;

https://github.com/alnao/AWSCloudFormationExamples/tree/master/Esempio11dynamoApiCrud

Per maggior informazioni sui tipi Serverless di CloudFormation si rimanda la documentazione ufficiale e agli articoli del blog ufficiale, perché bisogna sempre ricordare che questo tipo di risorse hanno dei parametri specifici studiati apposta per la creazione di applicazioni Serverless.


Per quanto riguarda la creazione delle Lambda con il tipo specifico AWS::Serverless::Function bisogna ricordare che questo tipo è diverso dal tipo base AWS::Lambda::Function, alcuni parametri sono uguali come ReservedConcurrentExecutions mentre altri sono diversi come il ProvisionedConcurrencyConfig, si rimanda sempre alla documentazione ufficiale per i dettagli di quali parametri sono previsti dal tipo serverless.

MENU