Skip to content

Commit

Permalink
Feature: Last Modified Filter Parameter (#249)
Browse files Browse the repository at this point in the history
* add and update test data

Signed-off-by: Jordan Dubrick <[email protected]>

* add new lastModified field to generated index

Signed-off-by: Jordan Dubrick <[email protected]>

* address gosec flags

Signed-off-by: Jordan Dubrick <[email protected]>

* add nosec exemp

Signed-off-by: Jordan Dubrick <[email protected]>

* update openapi spec and generated code

Signed-off-by: Jordan Dubrick <[email protected]>

* add filtering logic for last modified parameter

Signed-off-by: Jordan Dubrick <[email protected]>

* update vendor for server

Signed-off-by: Jordan Dubrick <[email protected]>

* add last modified file to test resources

Signed-off-by: Jordan Dubrick <[email protected]>

* make generator more readable

Signed-off-by: Jordan Dubrick <[email protected]>

* run codegen

Signed-off-by: Jordan Dubrick <[email protected]>

* address review changes

Signed-off-by: Jordan Dubrick <[email protected]>

* add new test case

Signed-off-by: Jordan Dubrick <[email protected]>

---------

Signed-off-by: Jordan Dubrick <[email protected]>
  • Loading branch information
Jdubrick authored Jun 28, 2024
1 parent ee1d190 commit 3fec1a2
Show file tree
Hide file tree
Showing 17 changed files with 1,282 additions and 77 deletions.
62 changes: 62 additions & 0 deletions index/generator/library/library.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"path"
"path/filepath"
"strings"
"time"

devfileParser "github.com/devfile/library/v2/pkg/devfile"
"github.com/devfile/library/v2/pkg/devfile/parser"
Expand Down Expand Up @@ -122,6 +123,10 @@ func GenerateIndexStruct(registryDirPath string, force bool) ([]schema.Schema, e
index = append(index, indexFromExtraDevfileEntries...)
}

index, err = SetLastModifiedValue(index, registryDirPath)
if err != nil {
return index, err
}
return index, nil
}

Expand Down Expand Up @@ -705,6 +710,63 @@ func validateStackInfo(stackInfo schema.Schema, stackfolderDir string) []error {
return errors
}

// SetLastModifiedValue adds the last modified value to a pre-created index
// The last modified dates are contained in a file named last_modified.json that is apart of the registry dir
/* #nosec G304 -- lastModFile is produced from filepath.Join which cleans the input path */
func SetLastModifiedValue(index []schema.Schema, registryDirPath string) ([]schema.Schema, error) {
lastModFile := filepath.Join(registryDirPath, "last_modified.json")
bytes, err := os.ReadFile(lastModFile)
if err != nil {
return index, err
}

var lastModifiedEntries schema.LastModifiedInfo
err = json.Unmarshal(bytes, &lastModifiedEntries)
if err != nil {
return index, err
}

lastModifiedEntriesMap := make(map[string]map[string]time.Time)

for idx := range lastModifiedEntries.Stacks {
updateLastModifiedMap(lastModifiedEntriesMap, &lastModifiedEntries.Stacks[idx])
}

for idx := range lastModifiedEntries.Samples {
updateLastModifiedMap(lastModifiedEntriesMap, &lastModifiedEntries.Samples[idx])
}

for i := range index {
var mostCurrentLastModifiedDate time.Time
for j := range index[i].Versions {
schemaItem := index[i] // a stack or sample
version := schemaItem.Versions[j]
versionNum := version.Version
lastModifiedDate := lastModifiedEntriesMap[schemaItem.Name][versionNum]
updateSchemaLastModified(&schemaItem, j, lastModifiedDate)
if lastModifiedDate.After(mostCurrentLastModifiedDate) {
mostCurrentLastModifiedDate = lastModifiedDate
}
}
// lastModified of a stack or sample will be the date any version of it was last changed
index[i].LastModified = mostCurrentLastModifiedDate.Format(time.RFC3339)
}

return index, nil
}

func updateLastModifiedMap(m map[string]map[string]time.Time, entry *schema.LastModifiedEntry) {
_, ok := m[entry.Name]
if !ok {
m[entry.Name] = make(map[string]time.Time)
}
m[entry.Name][entry.Version] = entry.LastModified
}

func updateSchemaLastModified(s *schema.Schema, versionIndx int, lastModifiedDate time.Time) {
s.Versions[versionIndx].LastModified = lastModifiedDate.Format(time.RFC3339)
}

// In checks if the value is in the array
func inArray(arr []string, value string) bool {
for _, item := range arr {
Expand Down
152 changes: 152 additions & 0 deletions index/generator/library/library_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -850,3 +850,155 @@ func TestCheckForRequiredMetadata(t *testing.T) {
}
}
}

func TestSetLastModifiedValue(t *testing.T) {
testRegistryDirPath := "../tests/registry"
index := []schema.Schema{
schema.Schema{
Name: "go",
DisplayName: "Go Runtime",
Description: "Stack with the latest Go version",
Type: "stack",
Tags: []string{"Go"},
Icon: "https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/golang.svg",
ProjectType: "go",
Language: "go",
Provider: "Red Hat",
Versions: []schema.Version{
schema.Version{
Version: "1.2.0",
SchemaVersion: "2.1.0",
Description: "Stack with the latest Go version with devfile v2.1.0 schema version",
Icon: "https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/golang.svg",
Tags: []string{"testtag"},
Links: map[string]string{"self": "devfile-catalog/go:1.2.0"},
Resources: []string{"devfile.yaml"},
StarterProjects: []string{"go-starter"},
CommandGroups: map[schema.CommandGroupKind]bool{"build": true, "debug": false, "deploy": false, "run": true, "test": false},
},
schema.Version{
Version: "1.1.0",
SchemaVersion: "2.0.0",
Description: "Stack with the latest Go version with devfile v2.0.0 schema version",
Icon: "https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/golang.svg",
Tags: []string{"Go"},
Links: map[string]string{"self": "devfile-catalog/go:1.1.0"},
Resources: []string{"devfile.yaml"},
StarterProjects: []string{"go-starter"},
CommandGroups: map[schema.CommandGroupKind]bool{"build": true, "debug": false, "deploy": false, "run": true, "test": false},
},
},
},
schema.Schema{
Name: "code-with-quarkus",
DisplayName: "Basic Quarkus",
Description: "A simple Hello World Java application using Quarkus",
Type: "sample",
Tags: []string{"Java", "Quarkus"},
Icon: "https://raw.githubusercontent.com/elsony/devfile-sample-code-with-quarkus/main/.devfile/icon/quarkus.png",
ProjectType: "quarkus",
Language: "java",
Provider: "Red Hat",
Versions: []schema.Version{
schema.Version{
Version: "1.1.0",
SchemaVersion: "2.0.0",
Description: "java quarkus with devfile v2.0.0",
Default: true,
Git: &schema.Git{
Remotes: map[string]string{"origin": "https://github.com/elsony/devfile-sample-code-with-quarkus.git"},
},
},
},
},
}

wantIndex := []schema.Schema{
schema.Schema{
Name: "go",
DisplayName: "Go Runtime",
Description: "Stack with the latest Go version",
Type: "stack",
Tags: []string{"Go"},
Icon: "https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/golang.svg",
ProjectType: "go",
Language: "go",
Provider: "Red Hat",
LastModified: "2023-11-08T12:54:08Z",
Versions: []schema.Version{
schema.Version{
Version: "1.2.0",
SchemaVersion: "2.1.0",
Description: "Stack with the latest Go version with devfile v2.1.0 schema version",
Icon: "https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/golang.svg",
Tags: []string{"testtag"},
Links: map[string]string{"self": "devfile-catalog/go:1.2.0"},
Resources: []string{"devfile.yaml"},
StarterProjects: []string{"go-starter"},
CommandGroups: map[schema.CommandGroupKind]bool{"build": true, "debug": false, "deploy": false, "run": true, "test": false},
LastModified: "2023-04-08T11:51:08Z",
},
schema.Version{
Version: "1.1.0",
SchemaVersion: "2.0.0",
Description: "Stack with the latest Go version with devfile v2.0.0 schema version",
Icon: "https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/golang.svg",
Tags: []string{"Go"},
Links: map[string]string{"self": "devfile-catalog/go:1.1.0"},
Resources: []string{"devfile.yaml"},
StarterProjects: []string{"go-starter"},
CommandGroups: map[schema.CommandGroupKind]bool{"build": true, "debug": false, "deploy": false, "run": true, "test": false},
LastModified: "2023-11-08T12:54:08Z",
},
},
},
schema.Schema{
Name: "code-with-quarkus",
DisplayName: "Basic Quarkus",
Description: "A simple Hello World Java application using Quarkus",
Type: "sample",
Tags: []string{"Java", "Quarkus"},
Icon: "https://raw.githubusercontent.com/elsony/devfile-sample-code-with-quarkus/main/.devfile/icon/quarkus.png",
ProjectType: "quarkus",
Language: "java",
Provider: "Red Hat",
LastModified: "2024-04-19T11:45:48+01:00",
Versions: []schema.Version{
schema.Version{
Version: "1.1.0",
SchemaVersion: "2.0.0",
Description: "java quarkus with devfile v2.0.0",
Default: true,
Git: &schema.Git{
Remotes: map[string]string{"origin": "https://github.com/elsony/devfile-sample-code-with-quarkus.git"},
},
LastModified: "2024-04-19T11:45:48+01:00",
},
},
},
}

tests := []struct {
name string
index []schema.Schema
wantIndex []schema.Schema
}{
{
name: "Test SetLastModifiedValue",
index: index,
wantIndex: wantIndex,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotIndex, err := SetLastModifiedValue(index, testRegistryDirPath)
if err != nil {
t.Errorf("Failed to set last modified value: %v", err)
}
if !reflect.DeepEqual(tt.wantIndex, gotIndex) {
t.Errorf("Generated index does not match")
}
})
}
}
24 changes: 22 additions & 2 deletions index/generator/schema/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
package schema

import (
"time"

apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
)

Expand All @@ -38,6 +40,7 @@ Sample index file:
],
"projectType": "maven",
"language": "java",
"lastModified": "2024-05-13T12:32:02+02:00",
"versions": [
{
"version": "1.1.0",
Expand All @@ -61,7 +64,8 @@ Sample index file:
],
"starterProjects": [
"springbootproject"
]
],
"lastModified": "2024-05-13T12:32:02+02:00"
}
]
},
Expand All @@ -79,6 +83,7 @@ Sample index file:
],
"projectType": "quarkus",
"language": "java",
"lastModified": "2024-04-29T17:08:43+03:00",
"versions": [
{
"version": "1.1.0",
Expand Down Expand Up @@ -108,7 +113,8 @@ Sample index file:
"starterProjects": [
"community",
"redhat-product"
]
],
"lastModified": "2024-04-29T17:08:43+03:00"
}
]
}
Expand Down Expand Up @@ -136,6 +142,7 @@ starterProjects: string[] - The project templates that can be used in the devfil
git: *git - The information of remote repositories
provider: string - The devfile provider information
versions: []Version - The list of stack versions information
lastModified: string - The date that a version of this stack/sample was last changed
*/

// Schema is the index file schema
Expand All @@ -161,6 +168,7 @@ type Schema struct {
Provider string `yaml:"provider,omitempty" json:"provider,omitempty"`
SupportUrl string `yaml:"supportUrl,omitempty" json:"supportUrl,omitempty"`
Versions []Version `yaml:"versions,omitempty" json:"versions,omitempty"`
LastModified string `yaml:"lastModified,omitempty" json:"lastModified,omitempty"`
}

// DevfileType describes the type of devfile
Expand Down Expand Up @@ -264,4 +272,16 @@ type Version struct {
DeploymentScopes map[DeploymentScopeKind]bool `yaml:"deploymentScopes,omitempty" json:"deploymentScopes,omitempty"`
Resources []string `yaml:"resources,omitempty" json:"resources,omitempty"`
StarterProjects []string `yaml:"starterProjects,omitempty" json:"starterProjects,omitempty"`
LastModified string `yaml:"lastModified,omitempty" json:"lastModified,omitempty"`
}

type LastModifiedEntry struct {
Name string `yaml:"name,omitempty" json:"name,omitempty"`
Version string `yaml:"version,omitempty" json:"version,omitempty"`
LastModified time.Time `yaml:"lastModified,omitempty" json:"lastModified,omitempty"`
}

type LastModifiedInfo struct {
Stacks []LastModifiedEntry `yaml:"stacks,omitempty" json:"stacks,omitempty"`
Samples []LastModifiedEntry `yaml:"samples,omitempty" json:"samples,omitempty"`
}
Loading

0 comments on commit 3fec1a2

Please sign in to comment.