-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
20 changed files
with
2,422 additions
and
69 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
.DS_Store | ||
.env | ||
.env | ||
*.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
*.yml | ||
*.yml | ||
*.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
package blogs | ||
|
||
import ( | ||
"encoding/json" | ||
"io/ioutil" | ||
"net/http" | ||
"sort" | ||
"strings" | ||
"time" | ||
"utils" | ||
|
||
"github.com/gin-gonic/gin" | ||
log "github.com/sirupsen/logrus" | ||
) | ||
|
||
const YYYYMMDD = "2006-01-02 15:04:05" | ||
const MAX_BLOG_NUMBER = 3 | ||
|
||
type Item struct { | ||
Title string `json:"title"` | ||
Author string `json:"author"` | ||
PubDate string `json:"pubDate"` | ||
Link string `json:"link"` | ||
GUID string `json:"guid"` | ||
Thumbnail string `json:"thumbnail"` | ||
Description string `json:"description"` | ||
} | ||
|
||
type Blog struct { | ||
Items []Item `json:"items"` | ||
} | ||
|
||
func Get(c *gin.Context) { | ||
|
||
// get blogs from API | ||
response, err := http.Get("https://api.rss2json.com/v1/api.json?rss_url=https://medium.com/feed/canopas") | ||
|
||
if err != nil { | ||
log.Error(err) | ||
c.AbortWithStatus(http.StatusInternalServerError) | ||
return | ||
} | ||
|
||
defer response.Body.Close() | ||
|
||
responseData, err := ioutil.ReadAll(response.Body) | ||
if err != nil { | ||
log.Error(err) | ||
c.AbortWithStatus(http.StatusInternalServerError) | ||
return | ||
} | ||
|
||
// bind data to struct | ||
var blogs Blog | ||
json.Unmarshal(responseData, &blogs) | ||
|
||
filteredItems := []Item{} | ||
fileName := "./blogs.json" | ||
|
||
// filter weekly and newletteres | ||
for _, item := range blogs.Items { | ||
if !strings.Contains(strings.ToLower(item.Title), "weekly") && !strings.Contains(strings.ToLower(item.Title), "newsletter") { | ||
filteredItems = append(filteredItems, item) | ||
} | ||
} | ||
|
||
// read and append blog from file, if blogs are less then 3 | ||
if len(filteredItems) < MAX_BLOG_NUMBER { | ||
filteredItems = append(filteredItems, utils.ReadSliceFromFile(fileName, []Item{})...) | ||
|
||
// make blogs unique and get only 3 blogs | ||
filteredItems = utils.Unique(filteredItems) | ||
} | ||
|
||
if len(filteredItems) > MAX_BLOG_NUMBER { | ||
filteredItems = filteredItems[0:MAX_BLOG_NUMBER] | ||
} | ||
|
||
// sort blogs by published date | ||
sortBlogs(filteredItems) | ||
|
||
// write blogs to file | ||
utils.WriteSliceToFile(fileName, filteredItems) | ||
|
||
c.JSON(http.StatusOK, filteredItems) | ||
} | ||
|
||
func sortBlogs(blogs []Item) { | ||
sort.Slice(blogs, func(i, j int) bool { | ||
dateI, err := time.Parse(YYYYMMDD, blogs[i].PubDate) | ||
if err != nil { | ||
log.Warn(err) | ||
} | ||
dateJ, err := time.Parse(YYYYMMDD, blogs[j].PubDate) | ||
if err != nil { | ||
log.Warn(err) | ||
} | ||
return dateI.After(dateJ) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package blogs | ||
|
||
import ( | ||
"net/http" | ||
"net/http/httptest" | ||
"testing" | ||
"utils" | ||
|
||
"github.com/gin-gonic/gin" | ||
"github.com/tj/assert" | ||
) | ||
|
||
func Test_Unique_Int_Success(t *testing.T) { | ||
got := utils.Unique([]int{1, 1, 2, 2, 3, 3}) | ||
expected := []int{1, 2, 3} | ||
assert.Equal(t, got, expected) | ||
} | ||
|
||
func Test_Unique_String_Fail(t *testing.T) { | ||
got := utils.Unique([]string{"apple", "has", "apple", "logo"}) | ||
expected := []string{"apple", "has", "apple", "logo"} | ||
assert.NotEqual(t, got, expected) | ||
} | ||
|
||
func Test_Unique_Struct_Success(t *testing.T) { | ||
got := utils.Unique([]Item{{ | ||
Title: "test1", | ||
}, { | ||
Title: "test1", | ||
}, { | ||
Title: "test2", | ||
}}) | ||
expected := []Item{{ | ||
Title: "test1", | ||
}, { | ||
Title: "test2", | ||
}} | ||
assert.Equal(t, got, expected) | ||
} | ||
|
||
func Test_ReadFile_Empty_Filepath(t *testing.T) { | ||
got := utils.ReadSliceFromFile("", []Item{}) | ||
expected := []Item{} | ||
assert.Equal(t, got, expected) | ||
} | ||
|
||
func Test_ReadFile_Wrong_Filepath(t *testing.T) { | ||
got := utils.ReadSliceFromFile("test.json", []Item{}) | ||
expected := []Item{} | ||
assert.Equal(t, got, expected) | ||
} | ||
|
||
func Test_GetBlogs_Integration(t *testing.T) { | ||
w := httptest.NewRecorder() | ||
req, err := http.NewRequest("GET", "/api/blogs", nil) | ||
|
||
assert.NoError(t, err) | ||
|
||
engine := gin.New() | ||
setUpRouter(engine) | ||
engine.ServeHTTP(w, req) | ||
assert.Equal(t, http.StatusOK, w.Code) | ||
} | ||
|
||
func setUpRouter(engine *gin.Engine) { | ||
engine.GET("/api/blogs", Get) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
module blogs | ||
|
||
go 1.20 | ||
|
||
replace db => ../db | ||
|
||
replace utils => ../utils | ||
|
||
require ( | ||
github.com/gin-gonic/gin v1.9.0 | ||
github.com/sirupsen/logrus v1.9.0 | ||
github.com/tj/assert v0.0.3 | ||
utils v0.0.0-00010101000000-000000000000 | ||
) | ||
|
||
require ( | ||
cloud.google.com/go/compute v1.7.0 // indirect | ||
cloud.google.com/go/recaptchaenterprise v1.3.1 // indirect | ||
cloud.google.com/go/recaptchaenterprise/v2 v2.0.1 // indirect | ||
db v0.0.0-00010101000000-000000000000 // indirect | ||
github.com/aws/aws-sdk-go v1.44.83 // indirect | ||
github.com/bytedance/sonic v1.8.0 // indirect | ||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect | ||
github.com/davecgh/go-spew v1.1.1 // indirect | ||
github.com/gin-contrib/sse v0.1.0 // indirect | ||
github.com/go-playground/locales v0.14.1 // indirect | ||
github.com/go-playground/universal-translator v0.18.1 // indirect | ||
github.com/go-playground/validator/v10 v10.11.2 // indirect | ||
github.com/go-sql-driver/mysql v1.6.0 // indirect | ||
github.com/goccy/go-json v0.10.0 // indirect | ||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect | ||
github.com/golang/protobuf v1.5.2 // indirect | ||
github.com/google/go-cmp v0.5.8 // indirect | ||
github.com/google/uuid v1.3.0 // indirect | ||
github.com/googleapis/enterprise-certificate-proxy v0.1.0 // indirect | ||
github.com/googleapis/gax-go/v2 v2.4.0 // indirect | ||
github.com/jmespath/go-jmespath v0.4.0 // indirect | ||
github.com/jmoiron/sqlx v1.3.5 // indirect | ||
github.com/json-iterator/go v1.1.12 // indirect | ||
github.com/klauspost/cpuid/v2 v2.0.9 // indirect | ||
github.com/leodido/go-urn v1.2.1 // indirect | ||
github.com/mattn/go-isatty v0.0.17 // indirect | ||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect | ||
github.com/modern-go/reflect2 v1.0.2 // indirect | ||
github.com/pelletier/go-toml/v2 v2.0.6 // indirect | ||
github.com/pmezard/go-difflib v1.0.0 // indirect | ||
github.com/stretchr/testify v1.8.1 // indirect | ||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect | ||
github.com/ugorji/go/codec v1.2.9 // indirect | ||
go.opencensus.io v0.23.0 // indirect | ||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect | ||
golang.org/x/crypto v0.5.0 // indirect | ||
golang.org/x/net v0.7.0 // indirect | ||
golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094 // indirect | ||
golang.org/x/sys v0.5.0 // indirect | ||
golang.org/x/text v0.7.0 // indirect | ||
google.golang.org/api v0.94.0 // indirect | ||
google.golang.org/appengine v1.6.7 // indirect | ||
google.golang.org/genproto v0.0.0-20220902135211-223410557253 // indirect | ||
google.golang.org/grpc v1.48.0 // indirect | ||
google.golang.org/protobuf v1.28.1 // indirect | ||
gopkg.in/yaml.v3 v3.0.1 // indirect | ||
) |
Oops, something went wrong.