2019-05-03 10:47:22 +00:00
// Copyright 2019 Sorint.lab
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
// See the License for the specific language governing permissions and
// limitations under the License.
2019-05-03 21:35:25 +00:00
package action
2019-05-03 10:47:22 +00:00
import (
"context"
"encoding/json"
2019-07-01 09:40:20 +00:00
"agola.io/agola/internal/datamanager"
"agola.io/agola/internal/db"
"agola.io/agola/internal/util"
2019-07-31 13:39:07 +00:00
"agola.io/agola/services/configstore/types"
2019-05-03 10:47:22 +00:00
2022-02-21 08:40:18 +00:00
"github.com/gofrs/uuid"
2019-05-23 09:23:14 +00:00
errors "golang.org/x/xerrors"
2019-05-03 10:47:22 +00:00
)
2019-05-12 22:23:08 +00:00
func ( h * ActionHandler ) GetVariables ( ctx context . Context , parentType types . ConfigType , parentRef string , tree bool ) ( [ ] * types . Variable , error ) {
var variables [ ] * types . Variable
2019-07-25 08:46:02 +00:00
err := h . readDB . Do ( ctx , func ( tx * db . Tx ) error {
2022-02-21 11:19:55 +00:00
parentID , err := h . ResolveConfigID ( tx , parentType , parentRef )
2019-05-12 22:23:08 +00:00
if err != nil {
return err
}
if tree {
variables , err = h . readDB . GetVariablesTree ( tx , parentType , parentID )
} else {
variables , err = h . readDB . GetVariables ( tx , parentID )
}
return err
} )
if err != nil {
return nil , err
}
return variables , nil
}
2019-07-06 13:25:24 +00:00
func ( h * ActionHandler ) ValidateVariable ( ctx context . Context , variable * types . Variable ) error {
2019-05-03 10:47:22 +00:00
if variable . Name == "" {
2022-02-21 11:19:55 +00:00
return util . NewAPIError ( util . ErrBadRequest , errors . Errorf ( "variable name required" ) )
2019-05-03 10:47:22 +00:00
}
if ! util . ValidateName ( variable . Name ) {
2022-02-21 11:19:55 +00:00
return util . NewAPIError ( util . ErrBadRequest , errors . Errorf ( "invalid variable name %q" , variable . Name ) )
2019-05-03 10:47:22 +00:00
}
if len ( variable . Values ) == 0 {
2022-02-21 11:19:55 +00:00
return util . NewAPIError ( util . ErrBadRequest , errors . Errorf ( "variable values required" ) )
2019-05-03 10:47:22 +00:00
}
if variable . Parent . Type == "" {
2022-02-21 11:19:55 +00:00
return util . NewAPIError ( util . ErrBadRequest , errors . Errorf ( "variable parent type required" ) )
2019-05-03 10:47:22 +00:00
}
if variable . Parent . ID == "" {
2022-02-21 11:19:55 +00:00
return util . NewAPIError ( util . ErrBadRequest , errors . Errorf ( "variable parent id required" ) )
2019-05-03 10:47:22 +00:00
}
if variable . Parent . Type != types . ConfigTypeProject && variable . Parent . Type != types . ConfigTypeProjectGroup {
2022-02-21 11:19:55 +00:00
return util . NewAPIError ( util . ErrBadRequest , errors . Errorf ( "invalid variable parent type %q" , variable . Parent . Type ) )
2019-07-06 13:25:24 +00:00
}
return nil
}
func ( h * ActionHandler ) CreateVariable ( ctx context . Context , variable * types . Variable ) ( * types . Variable , error ) {
if err := h . ValidateVariable ( ctx , variable ) ; err != nil {
return nil , err
2019-05-03 10:47:22 +00:00
}
var cgt * datamanager . ChangeGroupsUpdateToken
// changegroup is the variable name
cgNames := [ ] string { util . EncodeSha256Hex ( "variablename-" + variable . Name ) }
// must do all the checks in a single transaction to avoid concurrent changes
2019-07-25 08:46:02 +00:00
err := h . readDB . Do ( ctx , func ( tx * db . Tx ) error {
2019-05-03 10:47:22 +00:00
var err error
2019-05-03 21:35:25 +00:00
cgt , err = h . readDB . GetChangeGroupsUpdateTokens ( tx , cgNames )
2019-05-03 10:47:22 +00:00
if err != nil {
return err
}
2022-02-21 11:19:55 +00:00
parentID , err := h . ResolveConfigID ( tx , variable . Parent . Type , variable . Parent . ID )
2019-05-03 10:47:22 +00:00
if err != nil {
return err
}
variable . Parent . ID = parentID
// check duplicate variable name
2019-05-03 21:35:25 +00:00
s , err := h . readDB . GetVariableByName ( tx , variable . Parent . ID , variable . Name )
2019-05-03 10:47:22 +00:00
if err != nil {
return err
}
if s != nil {
2022-02-21 11:19:55 +00:00
return util . NewAPIError ( util . ErrBadRequest , errors . Errorf ( "variable with name %q for %s with id %q already exists" , variable . Name , variable . Parent . Type , variable . Parent . ID ) )
2019-05-03 10:47:22 +00:00
}
return nil
} )
if err != nil {
return nil , err
}
2022-02-21 08:40:18 +00:00
variable . ID = uuid . Must ( uuid . NewV4 ( ) ) . String ( )
2019-05-03 10:47:22 +00:00
variablej , err := json . Marshal ( variable )
if err != nil {
2019-05-23 09:23:14 +00:00
return nil , errors . Errorf ( "failed to marshal variable: %w" , err )
2019-05-03 10:47:22 +00:00
}
actions := [ ] * datamanager . Action {
{
ActionType : datamanager . ActionTypePut ,
DataType : string ( types . ConfigTypeVariable ) ,
ID : variable . ID ,
Data : variablej ,
} ,
}
2019-05-03 21:35:25 +00:00
_ , err = h . dm . WriteWal ( ctx , actions , cgt )
2019-05-03 10:47:22 +00:00
return variable , err
}
2019-07-06 13:25:24 +00:00
type UpdateVariableRequest struct {
VariableName string
Variable * types . Variable
}
func ( h * ActionHandler ) UpdateVariable ( ctx context . Context , req * UpdateVariableRequest ) ( * types . Variable , error ) {
if err := h . ValidateVariable ( ctx , req . Variable ) ; err != nil {
return nil , err
}
var curVariable * types . Variable
var cgt * datamanager . ChangeGroupsUpdateToken
// changegroup is the variable name
// must do all the checks in a single transaction to avoid concurrent changes
2019-07-25 08:46:02 +00:00
err := h . readDB . Do ( ctx , func ( tx * db . Tx ) error {
2019-07-06 13:25:24 +00:00
var err error
2022-02-21 11:19:55 +00:00
parentID , err := h . ResolveConfigID ( tx , req . Variable . Parent . Type , req . Variable . Parent . ID )
2019-07-06 13:25:24 +00:00
if err != nil {
return err
}
req . Variable . Parent . ID = parentID
// check variable exists
curVariable , err = h . readDB . GetVariableByName ( tx , req . Variable . Parent . ID , req . VariableName )
if err != nil {
return err
}
if curVariable == nil {
2022-02-21 11:19:55 +00:00
return util . NewAPIError ( util . ErrBadRequest , errors . Errorf ( "variable with name %q for %s with id %q doesn't exists" , req . VariableName , req . Variable . Parent . Type , req . Variable . Parent . ID ) )
2019-07-06 13:25:24 +00:00
}
if curVariable . Name != req . Variable . Name {
// check duplicate variable name
u , err := h . readDB . GetVariableByName ( tx , req . Variable . Parent . ID , req . Variable . Name )
if err != nil {
return err
}
if u != nil {
2022-02-21 11:19:55 +00:00
return util . NewAPIError ( util . ErrBadRequest , errors . Errorf ( "variable with name %q for %s with id %q already exists" , req . Variable . Name , req . Variable . Parent . Type , req . Variable . Parent . ID ) )
2019-07-06 13:25:24 +00:00
}
}
// set/override ID that must be kept from the current variable
req . Variable . ID = curVariable . ID
cgNames := [ ] string {
util . EncodeSha256Hex ( "variablename-" + req . Variable . ID ) ,
util . EncodeSha256Hex ( "variablename-" + req . Variable . Name ) ,
}
cgt , err = h . readDB . GetChangeGroupsUpdateTokens ( tx , cgNames )
if err != nil {
return err
}
return nil
} )
if err != nil {
return nil , err
}
variablej , err := json . Marshal ( req . Variable )
if err != nil {
return nil , errors . Errorf ( "failed to marshal variable: %w" , err )
}
actions := [ ] * datamanager . Action {
{
ActionType : datamanager . ActionTypePut ,
DataType : string ( types . ConfigTypeVariable ) ,
ID : req . Variable . ID ,
Data : variablej ,
} ,
}
_ , err = h . dm . WriteWal ( ctx , actions , cgt )
return req . Variable , err
}
2019-05-03 21:35:25 +00:00
func ( h * ActionHandler ) DeleteVariable ( ctx context . Context , parentType types . ConfigType , parentRef , variableName string ) error {
2019-05-03 10:47:22 +00:00
var variable * types . Variable
var cgt * datamanager . ChangeGroupsUpdateToken
// must do all the checks in a single transaction to avoid concurrent changes
2019-07-25 08:46:02 +00:00
err := h . readDB . Do ( ctx , func ( tx * db . Tx ) error {
2019-05-03 10:47:22 +00:00
var err error
2022-02-21 11:19:55 +00:00
parentID , err := h . ResolveConfigID ( tx , parentType , parentRef )
2019-05-03 10:47:22 +00:00
if err != nil {
return err
}
// check variable existance
2019-05-03 21:35:25 +00:00
variable , err = h . readDB . GetVariableByName ( tx , parentID , variableName )
2019-05-03 10:47:22 +00:00
if err != nil {
return err
}
if variable == nil {
2022-02-21 11:19:55 +00:00
return util . NewAPIError ( util . ErrBadRequest , errors . Errorf ( "variable with name %q doesn't exist" , variableName ) )
2019-05-03 10:47:22 +00:00
}
// changegroup is the variable id
cgNames := [ ] string { util . EncodeSha256Hex ( "variableid-" + variable . ID ) }
2019-05-03 21:35:25 +00:00
cgt , err = h . readDB . GetChangeGroupsUpdateTokens ( tx , cgNames )
2019-05-03 10:47:22 +00:00
if err != nil {
return err
}
return nil
} )
if err != nil {
return err
}
actions := [ ] * datamanager . Action {
{
ActionType : datamanager . ActionTypeDelete ,
DataType : string ( types . ConfigTypeVariable ) ,
ID : variable . ID ,
} ,
}
2019-05-03 21:35:25 +00:00
_ , err = h . dm . WriteWal ( ctx , actions , cgt )
2019-05-03 10:47:22 +00:00
return err
}