Skip to content

Commit

Permalink
feat: get proof media instead of biography
Browse files Browse the repository at this point in the history
  • Loading branch information
dadamu committed Aug 10, 2023
1 parent 2a00158 commit 7f4a191
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 44 deletions.
26 changes: 19 additions & 7 deletions apis/instagram/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ import (
"fmt"
"io"
"net/http"
"strings"
)

var (
fields = strings.Join([]string{
"id",
"username",
"media.limit(1){caption}",
}, ",")
)

// API allows to query data from the Instagram APIs
Expand All @@ -21,10 +30,10 @@ func NewAPI() *API {
}
}

// GetUser returns the User by access token provided by the user
func (api *API) GetUser(accessToken string) (*User, error) {
// GetUserMedia returns the latest user media by access token provided by the user
func (api *API) GetUserMedia(accessToken string) (*UserMedia, error) {
// Build the endpoint
endpoint := fmt.Sprintf("%s/me?fields=id,biography&accessToken=%s", api.endpoint, accessToken)
endpoint := fmt.Sprintf("%s/me?fields=%s&access_token=%s", api.endpoint, fields, accessToken)

// Build the request
req, err := http.NewRequest("GET", endpoint, nil)
Expand Down Expand Up @@ -56,10 +65,13 @@ func (api *API) GetUser(accessToken string) (*User, error) {
return nil, err
}

// Return the user
return NewUser(
response.ID,
if len(response.Media.Data) == 0 {
return nil, fmt.Errorf("failed to get user latest media")
}

// Return the user media
return NewUserMedia(
response.Username,
response.Biography,
response.Media.Data[0].Caption,
), nil
}
7 changes: 4 additions & 3 deletions apis/instagram/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ func (suite *ApiTestSuite) SetupSuite() {
}

func (suite *ApiTestSuite) TestGetUser() {
// user, err := suite.api.GetUser("leobragaz")
// suite.Require().NoError(err)
// suite.Require().NotEmpty(user.Username)
user, err := suite.api.GetUserMedia("IGQVJWejVfNDBQVkRBeVp4VDg0RkxFczJsVVR1NWZAudUNLRnQtV2RjckNBam1RcmFuWTlVT1dzZA0hnTFdfTHNZAQ3FrOVdXQTFJN2pzTmd3MmVHdHV0UWpmYUdsNEh3Y3VRZA012VlBUZAUdjTUtCeVZABaAZDZD")
suite.Require().NoError(err)
suite.Require().NotEmpty(user.Username)
suite.Require().NotEmpty(user.Caption)
}
15 changes: 12 additions & 3 deletions apis/instagram/api_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,16 @@ package instagram

// userResponse contains the data that are returned from the API used to get the info of a user
type userResponse struct {
ID string `json:"id"`
Username string `json:"username"`
Biography string `json:"biography"`
ID string `json:"id"`
Username string `json:"username"`
Media media `json:"media"`
}

type media struct {
Data []mediaData `json:"data"`
}

type mediaData struct {
ID string `json:"id"`
Caption string `json:"caption"`
}
12 changes: 6 additions & 6 deletions apis/instagram/gin.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,24 @@ import (
func RegisterGinHandler(r *gin.Engine, cfg *Config) {
handler := NewHandler(cfg.CacheFilePath, NewAPI())
r.Group("/instagram").
GET("/users/:username", func(c *gin.Context) {
user, err := handler.GetUser(c.Param("username"))
GET("/medias/:username", func(c *gin.Context) {
media, err := handler.GetUserMedia(c.Param("username"))
if err != nil {
c.AbortWithError(http.StatusBadRequest, err)
return
}

c.JSON(http.StatusOK, &user)
c.JSON(http.StatusOK, &media)
}).
POST("/users", func(c *gin.Context) {
var request AddUserRequest
POST("/medias", func(c *gin.Context) {
var request AddUserMediaRequest
err := c.BindJSON(&request)
if err != nil {
c.AbortWithError(http.StatusBadRequest, err)
return
}

err = handler.RequestUser(request.AccessToken)
err = handler.RequestUserMedia(request.AccessToken)
if err != nil {
c.AbortWithError(http.StatusBadRequest, err)
return
Expand Down
26 changes: 12 additions & 14 deletions apis/instagram/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ func NewHandler(cacheFilePath string, api *API) *Handler {

// cacheData represents how the Instagram data are stored inside the local cache
type cacheData struct {
Users map[string]*User // Maps the username to their user objects
Medias map[string]*UserMedia // Maps the username to their media objects
}

// newCacheData returns a new empty cacheData instance
func newCacheData() *cacheData {
return &cacheData{
Users: map[string]*User{},
Medias: map[string]*UserMedia{},
}
}

Expand All @@ -55,14 +55,14 @@ func (h *Handler) readCache() (*cacheData, error) {
}

// cacheUser caches the given user for future references
func (h *Handler) cacheUser(user *User) error {
func (h *Handler) cacheUser(user *UserMedia) error {
cache, err := h.readCache()
if err != nil {
return err
}

// Set the tweet
cache.Users[user.Username] = user
// Set the media
cache.Medias[user.Username] = user

// Serialize the contents
bz, err := json.Marshal(&cache)
Expand All @@ -75,23 +75,21 @@ func (h *Handler) cacheUser(user *User) error {
}

// getUserFromCache returns the User object associated with the user having the given user ID, if existing
func (h *Handler) getUserFromCache(username string) (*User, error) {
func (h *Handler) getUserFromCache(username string) (*UserMedia, error) {
cache, err := h.readCache()
if err != nil {
return nil, err
}

user, ok := cache.Users[username]
user, ok := cache.Medias[username]
if !ok {
return nil, nil
}
return user, nil
}

// GetUser returns the bio of the user having the given username, either from the cache if present of
// by querying the APIs.
// If the user was not cached, after retrieving it from the APIs it is later cached for future requests
func (h *Handler) GetUser(username string) (*User, error) {
// GetUserMedia returns the media of the user having the given username from cache.
func (h *Handler) GetUserMedia(username string) (*UserMedia, error) {
// Try getting the cached user
user, err := h.getUserFromCache(username)
if err != nil {
Expand All @@ -102,9 +100,9 @@ func (h *Handler) GetUser(username string) (*User, error) {
return user, nil
}

// RequestUser requests the instagram user profile from Meta Graph API then store it inside cache.
func (h *Handler) RequestUser(accessToken string) error {
user, err := h.api.GetUser(accessToken)
// RequestUserMedia requests the instagram user latest media from Instagram Graph API then store it inside cache.
func (h *Handler) RequestUserMedia(accessToken string) error {
user, err := h.api.GetUserMedia(accessToken)
if err != nil {
return err
}
Expand Down
20 changes: 9 additions & 11 deletions apis/instagram/types.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
package instagram

// User contains all the data of a user
type User struct {
ID string `json:"id"`
// UserMedia contains media data
type UserMedia struct {
Username string `json:"username"`
Bio string `json:"biography"`
Caption string `json:"caption"`
}

// NewUser builds a new User instance
func NewUser(id, username, biography string) *User {
return &User{
ID: id,
// NewUserMedia builds a new UserMedia instance
func NewUserMedia(username, biography string) *UserMedia {
return &UserMedia{
Username: username,
Bio: biography,
Caption: biography,
}
}

// AddUserRequest represents the access token that allows themis to ask profile information from instagram
type AddUserRequest struct {
// AddUserMediaRequest represents the access token that allows themis to ask user media from instagram
type AddUserMediaRequest struct {
AccessToken string `json:"accessToken"`
}

0 comments on commit 7f4a191

Please sign in to comment.