Skip to content

Commit

Permalink
added local file caching
Browse files Browse the repository at this point in the history
  • Loading branch information
mr-pmillz committed Sep 10, 2024
1 parent 0416601 commit 248e77c
Show file tree
Hide file tree
Showing 6 changed files with 222 additions and 81 deletions.
27 changes: 9 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ This is a wrapper around the endoflife.date API

## Installation

To install, just run the below command or download pre-compiled binary from the [releases page](https://github.com/mr-pmillz/eoldate/releases)
To install, run the following command or download a pre-compiled binary from the [releases page](https://github.com/mr-pmillz/eoldate/releases)

```shell
go install -v github.com/mr-pmillz/eoldate/cmd/eoldate@latest
Expand Down Expand Up @@ -58,25 +58,16 @@ import (

func main() {
client := eoldate.NewClient()
products, err := client.GetProduct("php")
softwareName := "php"
phpVersion := 8.2
isPHPEightPointTwoSupported, err := client.IsSupportedSoftwareVersion(softwareName, phpVersion)
if err != nil {
log.Fatalf("Error fetching product data: %v", err)
log.Fatal(err)
}

versionsToCheck := []float64{5.6, 7.4, 8.0, 8.1, 8.2}

for _, version := range versionsToCheck {
supported, err := products.IsVersionSupported(version)
if err != nil {
fmt.Printf("PHP %.1f: %v\n", version, err)
continue
}

if supported {
fmt.Printf("PHP %.1f is still supported\n", version)
} else {
fmt.Printf("PHP %.1f is no longer supported\n", version)
}
if isPHPEightPointTwoSupported {
fmt.Printf("%s %.1f is Supported", softwareName, phpVersion)
} else {
fmt.Printf("%s %.1f is not Supported", softwareName, phpVersion)
}
}
```
81 changes: 81 additions & 0 deletions cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package eoldate

import (
"fmt"
"os"
"path/filepath"
"time"
)

// readCache reads the cached data for a product
func readCache(product string) ([]byte, error) {
homeDir, err := os.UserHomeDir()
if err != nil {
return nil, LogError(err)
}
timestamp := time.Now().Format("01-02-2006")
cacheDir := filepath.Join(homeDir, ".config", "eoldate", "cache")
cacheFile := fmt.Sprintf("%s/%s-%s.json", cacheDir, product, timestamp)
if exists, err := Exists(cacheFile); err == nil && exists {
return os.ReadFile(cacheFile)
} else {
return nil, nil
}
}

// readAllTechnologiesCache ...
func readAllTechnologiesCache() ([]string, error) {
homeDir, err := os.UserHomeDir()
if err != nil {
return nil, LogError(err)
}
timestamp := time.Now().Format("01-02-2006")
cacheDir := filepath.Join(homeDir, ".config", "eoldate", "cache")
cacheAllTechFile := fmt.Sprintf("%s/all-technologies-%s.json", cacheDir, timestamp)
if exists, err := Exists(cacheAllTechFile); err == nil && exists {
return ReadLines(cacheAllTechFile)
} else {
return nil, nil
}
}

// writeCache writes data to the cache for a product
func writeCache(product string, data []byte) error {
homeDir, err := os.UserHomeDir()
if err != nil {
return LogError(err)
}
timestamp := time.Now().Format("01-02-2006")
cacheDir := filepath.Join(homeDir, ".config", "eoldate", "cache")
cacheFile := fmt.Sprintf("%s/%s-%s.json", cacheDir, product, timestamp)
return os.WriteFile(cacheFile, data, 0600)
}

// CacheTechnologies caches all available technologies to choose from to a local file cache
func (c *Client) CacheTechnologies() ([]string, error) {
homeDir, err := os.UserHomeDir()
if err != nil {
return nil, LogError(err)
}
timestamp := time.Now().Format("01-02-2006")
cacheDir := filepath.Join(homeDir, ".config", "eoldate", "cache")
allTechnologiesFileCache := fmt.Sprintf("%s/all-technologies-%s.json", cacheDir, timestamp)
if exists, err := Exists(cacheDir); err == nil && !exists {
if err = os.MkdirAll(cacheDir, 0755); err != nil {
return nil, LogError(err)
}
}

if cacheExists, err := Exists(allTechnologiesFileCache); err == nil && cacheExists {
return ReadLines(allTechnologiesFileCache)
}
allProducts, err := c.GetAllProducts()
if err != nil {
return nil, LogError(err)
}
if err = WriteLines(allProducts, allTechnologiesFileCache); err != nil {
return nil, LogError(err)
}

return allProducts, nil
}
12 changes: 3 additions & 9 deletions cmd/eoldate/main.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package main

import (
"encoding/json"
"flag"
"fmt"
"github.com/olekukonko/tablewriter"
Expand Down Expand Up @@ -63,22 +62,17 @@ func main() {
os.Exit(1)
}

data, err := client.Get(fmt.Sprintf("%s.json", eolOptions.Tech))
data, err := client.GetProduct(eolOptions.Tech)
if err != nil {
gologger.Fatal().Msgf("Error fetching product data: %v", err)
}

var products []eoldate.Product
if err = json.Unmarshal(data, &products); err != nil {
gologger.Fatal().Msgf("Error parsing JSON: %v", err)
}

tableBuilder := NewTableBuilder(products)
tableBuilder := NewTableBuilder(data)
tableString := tableBuilder.Render()
fmt.Println(tableString)

if eolOptions.Output != "" {
writeOutputFiles(eolOptions, tableString, products)
writeOutputFiles(eolOptions, tableString, data)
}
}

Expand Down
84 changes: 47 additions & 37 deletions eoldate.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ import (
"fmt"
"io"
"net/http"
"slices"
"strconv"
"time"
)

const (
CurrentVersion = `v0.0.9`
CurrentVersion = `v1.0.0`
EOLBaseURL = "https://endoflife.date/api"
NotAvailable = "N/A"
)
Expand Down Expand Up @@ -39,38 +40,6 @@ type Product struct {
AdditionalFields map[string]interface{} `json:"-"`
}

// UnmarshalJSON implements the json.Unmarshaler interface
func (p *Product) UnmarshalJSON(data []byte) error {
type ProductAlias Product
alias := &struct {
*ProductAlias
AdditionalFields map[string]interface{} `json:"-"`
}{
ProductAlias: (*ProductAlias)(p),
}

if err := json.Unmarshal(data, &alias); err != nil {
return err
}

var raw map[string]interface{}
if err := json.Unmarshal(data, &raw); err != nil {
return err
}

p.AdditionalFields = make(map[string]interface{})
for k, v := range raw {
switch k {
case "cycle", "releaseDate", "eol", "latest", "link", "latestReleaseDate", "lts", "support", "extendedSupport", "minJavaVersion", "supportedPHPVersions":
// These fields are already handled by the struct
default:
p.AdditionalFields[k] = v
}
}

return nil
}

// GetSupportedPHPVersions returns the supported PHP versions as a string
func (p *Product) GetSupportedPHPVersions() string {
switch v := p.SupportedPHPVersions.(type) {
Expand All @@ -83,6 +52,19 @@ func (p *Product) GetSupportedPHPVersions() string {
}
}

// IsSupportedSoftwareVersion ...
func (c *Client) IsSupportedSoftwareVersion(softwareName string, version float64) (bool, error) {
softwareReleaseData, err := c.GetProduct(softwareName)
if err != nil {
return false, LogError(err)
}
isSupported, err := softwareReleaseData.IsVersionSupported(version)
if err != nil {
return false, LogError(err)
}
return isSupported, nil
}

// IsVersionSupported checks if the given version is supported in any of the product cycles
func (p Products) IsVersionSupported(version float64) (bool, error) {
for _, product := range p {
Expand Down Expand Up @@ -165,18 +147,46 @@ type Products []Product

// GetProduct fetches the end-of-life information for a specific product.
func (c *Client) GetProduct(product string) (Products, error) {
data, err := c.Get(fmt.Sprintf("%s.json", product))
allProducts, err := c.CacheTechnologies()
if err != nil {
return nil, err
}
if slices.Contains(allProducts, product) {
var products Products
productCache, err := readCache(product)
if err != nil {
return nil, err
}
if productCache != nil {
err = json.Unmarshal(productCache, &products)
return products, err
}
data, err := c.Get(fmt.Sprintf("%s.json", product))
if err != nil {
return nil, err
}

var products Products
err = json.Unmarshal(data, &products)
return products, err
if err = json.Unmarshal(data, &products); err != nil {
return nil, err
}
if err = writeCache(product, data); err != nil {
return nil, err
}
return products, err
} else {
return nil, fmt.Errorf("product %s not found", product)
}
}

// GetAllProducts fetches the end-of-life information for all products.
func (c *Client) GetAllProducts() (AllProducts, error) {
allProductsCache, err := readAllTechnologiesCache()
if err != nil {
return nil, LogError(err)
}
if allProductsCache != nil {
return allProductsCache, nil
}
data, err := c.Get("all.json")
if err != nil {
return nil, err
Expand Down
25 changes: 8 additions & 17 deletions examples/is-supported/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,15 @@ import (

func main() {
client := eoldate.NewClient()
products, err := client.GetProduct("php")
softwareName := "php"
phpVersion := 8.2
isPHPEightPointTwoSupported, err := client.IsSupportedSoftwareVersion(softwareName, phpVersion)
if err != nil {
log.Fatalf("Error fetching product data: %v", err)
log.Fatal(err)
}

versionsToCheck := []float64{5.6, 7.4, 8.0, 8.1, 8.2}

for _, version := range versionsToCheck {
supported, err := products.IsVersionSupported(version)
if err != nil {
fmt.Printf("PHP %.1f: %v\n", version, err)
continue
}

if supported {
fmt.Printf("PHP %.1f is still supported\n", version)
} else {
fmt.Printf("PHP %.1f is no longer supported\n", version)
}
if isPHPEightPointTwoSupported {
fmt.Printf("%s %.1f is Supported", softwareName, phpVersion)
} else {
fmt.Printf("%s %.1f is not Supported", softwareName, phpVersion)
}
}
Loading

0 comments on commit 248e77c

Please sign in to comment.