Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 118 additions & 1 deletion pkg/plugin/GlobalPluginService.go
Original file line number Diff line number Diff line change
Expand Up @@ -1753,6 +1753,116 @@ func (impl *GlobalPluginServiceImpl) GetPluginParentMetadataDtos(parentIdVsPlugi
return pluginParentMetadataDtos, nil
}

func buildPluginStepsByPluginId(pluginSteps []*repository.PluginStep, pluginStepVariables []*repository.PluginStepVariable,
pluginStepConditions []*repository.PluginStepCondition, pluginScripts []*repository.PluginPipelineScript,
scriptMappings []*repository.ScriptPathArgPortMapping) map[int][]*bean2.PluginStepsDto {
pluginStepsDto := adaptor.GetPluginStepsDtoFromDbObjects(pluginSteps)
pluginStepVariablesDto := adaptor.GetPluginStepVariablesDtoFromDbObjects(pluginStepVariables)
pluginStepConditionsDto := adaptor.GetPluginStepConditionsDtoFromDbObjects(pluginStepConditions)
pluginScriptsDto := adaptor.GetPluginPipelineScriptsDtoFromDbObjects(pluginScripts)
scriptMappingsDto := adaptor.GetScripPathArgPortMappingsDtoFromDbObjects(scriptMappings)

variableIdVsConditionsMap := helper2.GetVariableIdVsPluginStepConditionsMap(pluginStepConditionsDto)
for _, variable := range pluginStepVariablesDto {
variable.PluginStepCondition = variableIdVsConditionsMap[variable.Id]
}
pluginStepIdVsVariablesMap := helper2.GetPluginStepIdVsPluginStepVariablesMap(pluginStepVariablesDto)

scriptIdVsMappingsMap := helper2.GetScriptIdVsScriptArgsDetailsMap(scriptMappingsDto)
for _, script := range pluginScriptsDto {
script.PathArgPortMapping = scriptIdVsMappingsMap[script.Id]
}
scriptIdVsScriptMap := helper2.GetScriptIdVsPluginScript(pluginScriptsDto)

pluginIdVsStepsMap := make(map[int][]*bean2.PluginStepsDto)
for index, step := range pluginStepsDto {
step.PluginStepVariable = pluginStepIdVsVariablesMap[step.Id]
step.PluginPipelineScript = scriptIdVsScriptMap[step.ScriptId]
pluginId := pluginSteps[index].PluginId
pluginIdVsStepsMap[pluginId] = append(pluginIdVsStepsMap[pluginId], step)
}
return pluginIdVsStepsMap
}

func (impl *GlobalPluginServiceImpl) getPluginStepsByPluginIds(pluginIds []int) (map[int][]*bean2.PluginStepsDto, error) {
pluginSteps, err := impl.globalPluginRepository.GetStepsByPluginIds(pluginIds)
if err != nil {
impl.logger.Errorw("getPluginStepsByPluginIds, error in getting plugin steps", "pluginIds", pluginIds, "err", err)
return nil, err
}
if len(pluginSteps) == 0 {
return map[int][]*bean2.PluginStepsDto{}, nil
}

stepIds := make([]int, 0, len(pluginSteps))
scriptIds := make([]int, 0, len(pluginSteps))
seenScriptIds := make(map[int]bool, len(pluginSteps))
for _, step := range pluginSteps {
stepIds = append(stepIds, step.Id)
if step.ScriptId > 0 && !seenScriptIds[step.ScriptId] {
scriptIds = append(scriptIds, step.ScriptId)
seenScriptIds[step.ScriptId] = true
}
}

pluginStepVariables, err := impl.globalPluginRepository.GetVariablesByStepIds(stepIds)
if err != nil {
impl.logger.Errorw("getPluginStepsByPluginIds, error in getting plugin step variables", "stepIds", stepIds, "err", err)
return nil, err
}
pluginStepConditions, err := impl.globalPluginRepository.GetConditionsByStepIds(stepIds)
if err != nil {
impl.logger.Errorw("getPluginStepsByPluginIds, error in getting plugin step conditions", "stepIds", stepIds, "err", err)
return nil, err
}

var pluginScripts []*repository.PluginPipelineScript
var scriptMappings []*repository.ScriptPathArgPortMapping
if len(scriptIds) > 0 {
pluginScripts, err = impl.globalPluginRepository.GetScriptDetailByIds(scriptIds)
if err != nil {
impl.logger.Errorw("getPluginStepsByPluginIds, error in getting plugin scripts", "scriptIds", scriptIds, "err", err)
return nil, err
}
scriptMappings, err = impl.globalPluginRepository.GetScriptMappingDetailByScriptIds(scriptIds)
if err != nil {
impl.logger.Errorw("getPluginStepsByPluginIds, error in getting plugin script mappings", "scriptIds", scriptIds, "err", err)
return nil, err
}
}

return buildPluginStepsByPluginId(pluginSteps, pluginStepVariables, pluginStepConditions, pluginScripts, scriptMappings), nil
}

func (impl *GlobalPluginServiceImpl) populateCustomPluginSteps(pluginParentMetadataDtos []*bean2.PluginParentMetadataDto) error {
customPluginVersionIds := make([]int, 0)
for _, parentPlugin := range pluginParentMetadataDtos {
if parentPlugin.Type != string(repository.PLUGIN_TYPE_SHARED) || parentPlugin.Versions == nil {
continue
}
for _, version := range parentPlugin.Versions.DetailedPluginVersionData {
customPluginVersionIds = append(customPluginVersionIds, version.Id)
}
}
if len(customPluginVersionIds) == 0 {
return nil
}

pluginIdVsStepsMap, err := impl.getPluginStepsByPluginIds(customPluginVersionIds)
if err != nil {
return err
}
for _, parentPlugin := range pluginParentMetadataDtos {
if parentPlugin.Type != string(repository.PLUGIN_TYPE_SHARED) || parentPlugin.Versions == nil {
continue
}
for _, version := range parentPlugin.Versions.DetailedPluginVersionData {
version.PluginSteps = pluginIdVsStepsMap[version.Id]
}
}
return nil
}

func (impl *GlobalPluginServiceImpl) ListAllPluginsV2(filter *bean2.PluginsListFilter) (*bean2.PluginsDto, error) {
impl.logger.Infow("request received, ListAllPluginsV2", "filter", filter)
pluginVersionsMetadata, err := impl.globalPluginRepository.GetMetaDataForAllPlugins(true)
Expand Down Expand Up @@ -1873,6 +1983,13 @@ func (impl *GlobalPluginServiceImpl) GetPluginDetailV2(queryParams bean2.GlobalP
impl.logger.Errorw("GetPluginDetailV2, error in getting plugin parent metadata dtos by pluginParentMetadata ids", "pluginParentMetadataIds", pluginParentMetadataIds, "err", err)
return nil, err
}
if queryParams.FetchPluginSteps {
err = impl.populateCustomPluginSteps(pluginParentMetadataDtos)
if err != nil {
impl.logger.Errorw("GetPluginDetailV2, error in getting custom plugin steps", "pluginParentMetadataIds", pluginParentMetadataIds, "err", err)
return nil, err
}
}

pluginsDto := bean2.NewPluginsDto().WithParentPlugins(pluginParentMetadataDtos)
return pluginsDto, nil
Expand Down Expand Up @@ -2220,7 +2337,7 @@ func (impl *GlobalPluginServiceImpl) createNewPluginVersionOfExistingPlugin(tx *
return 0, err
}
// before saving new plugin version marking previous version's isLatest as false.
err = impl.globalPluginRepository.MarkPreviousPluginVersionLatestFalse(pluginParentMinData.Id)
err = impl.globalPluginRepository.MarkPreviousPluginVersionLatestFalse(pluginParentMinData.Id, tx)
if err != nil {
impl.logger.Errorw("createNewPluginVersionOfExistingPlugin, error in MarkPreviousPluginVersionLatestFalse", "pluginParentId", pluginDto.Id, "err", err)
return 0, err
Expand Down
78 changes: 78 additions & 0 deletions pkg/plugin/GlobalPluginService_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright (c) 2026. Devtron Inc.
*
* 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.
*/

package plugin

import (
"testing"

"github.com/devtron-labs/devtron/pkg/plugin/repository"
)

func TestBuildPluginStepsByPluginId(t *testing.T) {
pluginSteps := []*repository.PluginStep{
{Id: 1, PluginId: 10, Name: "inline", Index: 1, StepType: repository.PLUGIN_STEP_TYPE_INLINE, ScriptId: 100},
{Id: 2, PluginId: 11, Name: "reference", Index: 1, StepType: repository.PLUGIN_STEP_TYPE_REF_PLUGIN, RefPluginId: 20},
}
pluginStepVariables := []*repository.PluginStepVariable{
{Id: 101, PluginStepId: 1, Name: "input", VariableType: repository.PLUGIN_VARIABLE_TYPE_INPUT},
{Id: 102, PluginStepId: 1, Name: "output", VariableType: repository.PLUGIN_VARIABLE_TYPE_OUTPUT},
}
pluginStepConditions := []*repository.PluginStepCondition{
{Id: 201, PluginStepId: 1, ConditionVariableId: 102, ConditionalOperator: "==", ConditionalValue: "success"},
}
pluginScripts := []*repository.PluginPipelineScript{
{Id: 100, Script: "echo hello", Type: repository.SCRIPT_TYPE_SHELL},
}
scriptMappings := []*repository.ScriptPathArgPortMapping{
{Id: 301, ScriptId: 100, TypeOfMapping: repository.SCRIPT_MAPPING_TYPE_PORT, PortOnLocal: 8080, PortOnContainer: 80},
}

stepsByPluginId := buildPluginStepsByPluginId(pluginSteps, pluginStepVariables, pluginStepConditions, pluginScripts, scriptMappings)

customPluginSteps := stepsByPluginId[10]
if len(customPluginSteps) != 1 {
t.Fatalf("expected one step for plugin 10, got %d", len(customPluginSteps))
}
step := customPluginSteps[0]
if step.Name != "inline" || step.PluginPipelineScript == nil || step.PluginPipelineScript.Script != "echo hello" {
t.Fatalf("unexpected hydrated step: %#v", step)
}
if len(step.PluginStepVariable) != 2 {
t.Fatalf("expected two variables, got %d", len(step.PluginStepVariable))
}
if len(step.PluginStepVariable[0].PluginStepCondition) != 0 {
t.Fatal("did not expect conditions on the input variable")
}
if len(step.PluginStepVariable[1].PluginStepCondition) != 1 || step.PluginStepVariable[1].PluginStepCondition[0].Id != 201 {
t.Fatalf("expected the output condition to be preserved: %#v", step.PluginStepVariable[1].PluginStepCondition)
}
if len(step.PluginPipelineScript.PathArgPortMapping) != 1 || step.PluginPipelineScript.PathArgPortMapping[0].Id != 301 {
t.Fatalf("expected the script mapping to be preserved: %#v", step.PluginPipelineScript.PathArgPortMapping)
}

referencePluginSteps := stepsByPluginId[11]
if len(referencePluginSteps) != 1 || referencePluginSteps[0].PluginPipelineScript != nil {
t.Fatalf("expected a reference step without an inline script: %#v", referencePluginSteps)
}
}

func TestBuildPluginStepsByPluginIdEmpty(t *testing.T) {
stepsByPluginId := buildPluginStepsByPluginId(nil, nil, nil, nil, nil)
if len(stepsByPluginId) != 0 {
t.Fatalf("expected no plugin steps, got %#v", stepsByPluginId)
}
}
17 changes: 17 additions & 0 deletions pkg/plugin/adaptor/adaptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,23 @@ func GetPluginStepVariablesDtoFromDbObjects(pluginStepVarsDbObj []*repository.Pl
return pluginStepVarsDto
}

// GetPluginStepConditionsDtoFromDbObjects converts persisted step conditions to API DTOs.
func GetPluginStepConditionsDtoFromDbObjects(pluginStepConditionsDbObj []*repository.PluginStepCondition) []*pluginBean.PluginStepCondition {
pluginStepConditionsDto := make([]*pluginBean.PluginStepCondition, 0, len(pluginStepConditionsDbObj))
for _, condition := range pluginStepConditionsDbObj {
pluginStepConditionsDto = append(pluginStepConditionsDto, &pluginBean.PluginStepCondition{
Id: condition.Id,
PluginStepId: condition.PluginStepId,
ConditionVariableId: condition.ConditionVariableId,
ConditionType: condition.ConditionType,
ConditionalOperator: condition.ConditionalOperator,
ConditionalValue: condition.ConditionalValue,
Deleted: condition.Deleted,
})
}
return pluginStepConditionsDto
}

// GetPluginPipelineScriptDtoFromDbObject returns PluginPipelineScript dto object without ScriptPathArgPortMapping object
func GetPluginPipelineScriptDtoFromDbObject(pluginPipelineScript *repository.PluginPipelineScript) *pluginBean.PluginPipelineScript {
if pluginPipelineScript == nil {
Expand Down
1 change: 1 addition & 0 deletions pkg/plugin/bean/bean.go
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,7 @@ type GlobalPluginDetailsRequest struct {
PluginIds []int `schema:"pluginId" json:"pluginIds"`
ParentPluginIds []int `schema:"parentPluginId" json:"parentPluginIds"`
FetchAllVersionDetails bool `schema:"fetchAllVersionDetails" json:"fetchAllVersionDetails"`
FetchPluginSteps bool `schema:"fetchPluginSteps" json:"fetchPluginSteps"`
ParentPluginIdentifier string `schema:"parentPluginIdentifier"` // comma separated parentPluginIdentifiers
ParentPluginIdentifiers []string `schema:"-" json:"parentPluginIdentifiers"`
AppId int `schema:"appId" json:"appId"`
Expand Down
9 changes: 9 additions & 0 deletions pkg/plugin/helper/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,15 @@ func GetPluginStepIdVsPluginStepVariablesMap(pluginStepVariables []*bean.PluginV
return pluginStepIdVsPluginStepVariablesMap
}

// GetVariableIdVsPluginStepConditionsMap groups conditions by the variable on which they are defined.
func GetVariableIdVsPluginStepConditionsMap(pluginStepConditions []*bean.PluginStepCondition) map[int][]*bean.PluginStepCondition {
variableIdVsPluginStepConditionsMap := make(map[int][]*bean.PluginStepCondition, len(pluginStepConditions))
for _, condition := range pluginStepConditions {
variableIdVsPluginStepConditionsMap[condition.ConditionVariableId] = append(variableIdVsPluginStepConditionsMap[condition.ConditionVariableId], condition)
}
return variableIdVsPluginStepConditionsMap
}

func GetScriptIdVsScriptArgsDetailsMap(scriptArgDetails []*bean.ScriptPathArgPortMapping) map[int][]*bean.ScriptPathArgPortMapping {
scriptIdVsScriptArgsDetailsMap := make(map[int][]*bean.ScriptPathArgPortMapping, len(scriptArgDetails))
for _, scriptArgDetail := range scriptArgDetails {
Expand Down
37 changes: 30 additions & 7 deletions pkg/plugin/repository/GlobalPluginRepository.go
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,7 @@ type GlobalPluginRepository interface {
GetExposedVariablesByPluginId(pluginId int) ([]*PluginStepVariable, error)
GetExposedVariablesForAllPlugins() ([]*PluginStepVariable, error)
GetConditionsByStepId(stepId int) ([]*PluginStepCondition, error)
GetConditionsByStepIds(stepIds []int) ([]*PluginStepCondition, error)
GetPluginByName(pluginName string) ([]*PluginMetadata, error)
GetAllPluginMetaData() ([]*PluginMetadata, error)
GetPluginStepsByPluginId(pluginId int) ([]*PluginStep, error)
Expand All @@ -404,7 +405,7 @@ type GlobalPluginRepository interface {
GetAllPluginMinData() ([]*PluginParentMetadata, error)
GetAllPluginMinDataByType(pluginType string) ([]*PluginParentMetadata, error)
GetPluginParentMinDataById(id int) (*PluginParentMetadata, error)
MarkPreviousPluginVersionLatestFalse(pluginParentId int) error
MarkPreviousPluginVersionLatestFalse(pluginParentId int, tx *pg.Tx) error
GetPluginMetadataByPluginIdentifier(identifier string) (*PluginMetadata, error)

SavePluginMetadata(pluginMetadata *PluginMetadata, tx *pg.Tx) (*PluginMetadata, error)
Expand Down Expand Up @@ -545,7 +546,9 @@ func (impl *GlobalPluginRepositoryImpl) GetStepsByPluginIds(pluginIds []int) ([]
var pluginSteps []*PluginStep
err := impl.dbConnection.Model(&pluginSteps).
Where("deleted = ?", false).
Where("plugin_id in (?)", pg.In(pluginIds)).Select()
Where("plugin_id in (?)", pg.In(pluginIds)).
Order("plugin_id, index, id").
Select()
if err != nil {
impl.logger.Errorw("err in getting plugin steps by pluginIds", "err", err, "pluginIds", pluginIds)
return nil, err
Expand All @@ -569,7 +572,9 @@ func (impl *GlobalPluginRepositoryImpl) GetScriptDetailByIds(ids []int) ([]*Plug
var scriptDetail []*PluginPipelineScript
err := impl.dbConnection.Model(&scriptDetail).
Where("id in (?)", pg.In(ids)).
Where("deleted = ?", false).Select()
Where("deleted = ?", false).
Order("id").
Select()
if err != nil {
impl.logger.Errorw("err in getting script detail by ids", "ids", ids, "err", err)
return nil, err
Expand All @@ -593,7 +598,9 @@ func (impl *GlobalPluginRepositoryImpl) GetScriptMappingDetailByScriptIds(script
var scriptMappingDetail []*ScriptPathArgPortMapping
err := impl.dbConnection.Model(&scriptMappingDetail).
Where("script_id in (?)", pg.In(scriptIds)).
Where("deleted = ?", false).Select()
Where("deleted = ?", false).
Order("script_id, id").
Select()
if err != nil {
impl.logger.Errorw("err in getting script mapping detail by id", "scriptIds", scriptIds, "err", err)
return nil, err
Expand All @@ -617,7 +624,9 @@ func (impl *GlobalPluginRepositoryImpl) GetVariablesByStepIds(stepIds []int) ([]
var variables []*PluginStepVariable
err := impl.dbConnection.Model(&variables).
Where("plugin_step_id in (?)", pg.In(stepIds)).
Where("deleted = ?", false).Select()
Where("deleted = ?", false).
Order("plugin_step_id, variable_step_index, id").
Select()
if err != nil {
impl.logger.Errorw("err in getting variables by stepIds", "stepIds", stepIds, "err", err)
return nil, err
Expand Down Expand Up @@ -689,6 +698,20 @@ func (impl *GlobalPluginRepositoryImpl) GetConditionsByStepId(stepId int) ([]*Pl
return conditions, nil
}

func (impl *GlobalPluginRepositoryImpl) GetConditionsByStepIds(stepIds []int) ([]*PluginStepCondition, error) {
var conditions []*PluginStepCondition
err := impl.dbConnection.Model(&conditions).
Where("plugin_step_id in (?)", pg.In(stepIds)).
Where("deleted = ?", false).
Order("plugin_step_id, condition_variable_id, id").
Select()
if err != nil {
impl.logger.Errorw("err in getting conditions by stepIds", "stepIds", stepIds, "err", err)
return nil, err
}
return conditions, nil
}

func (impl *GlobalPluginRepositoryImpl) GetPluginByName(pluginName string) ([]*PluginMetadata, error) {
var plugin []*PluginMetadata
err := impl.dbConnection.Model(&plugin).
Expand Down Expand Up @@ -1039,9 +1062,9 @@ func (impl *GlobalPluginRepositoryImpl) GetAllPluginMinDataByType(pluginType str
return plugins, nil
}

func (impl *GlobalPluginRepositoryImpl) MarkPreviousPluginVersionLatestFalse(pluginParentId int) error {
func (impl *GlobalPluginRepositoryImpl) MarkPreviousPluginVersionLatestFalse(pluginParentId int, tx *pg.Tx) error {
var model PluginMetadata
_, err := impl.dbConnection.Model(&model).
_, err := tx.Model(&model).
Set("is_latest = ?", false).
Where("id = (select id from plugin_metadata where plugin_parent_metadata_id = ? and is_latest =true order by created_on desc limit ?)", pluginParentId, 1).
Update()
Expand Down
Loading