package repositories import ( "db_service/models" helper "db_service/pkg" "encoding/json" "errors" "fmt" "net/http" "reflect" "regexp" "strconv" "strings" ) type LinkParser struct { link string } func NewLinkParser(link string) LinkParser { return LinkParser{link: link} } func (l LinkParser) ParseLink() (models.TrendyolProductDetailModel, error) { helper.Info("link: ", l.link) productId := "" if isShortLink(l.link) { productId = getProductIdFromShortLink(l.link) } else { productId = getProductIdFromLink(l.link) } if len(productId) == 0 { parseErr := errors.New("can not parse product id") helper.Error(parseErr) return models.TrendyolProductDetailModel{}, parseErr } helper.Info("productId: ", productId) return GetProductDetails(productId) } func getProductIdFromShortLink(shortLink string) string { var productId string client, _ := helper.NewHttpClient() req, err := http.NewRequest("GET", shortLink, nil) req.Proto = "HTTP/2.0" if err != nil { helper.Error(err) return "" } q := req.URL.Query() req.URL.RawQuery = q.Encode() req.Header.Set("Connection", "close") req = req.WithContext(req.Context()) req.Close = true client.CloseIdleConnections() response, err := client.Do(req) if err != nil { helper.Error(err) return productId } defer response.Body.Close() url := response.Request.URL.Path helper.Info("link url: ", url) productId = getProductIdFromLink(url) helper.Info("productId: ", productId) return productId } func getProductIdFromLink(link string) string { var productId string if strings.Contains(link, "?") { link = strings.Split(link, "?")[0] } strArr := strings.Split(link, "-") productId = strArr[len(strArr)-1] return productId } func isShortLink(link string) bool { return !strings.Contains(link, "trendyol.com") } // GetProductDetails return JSON object. Merges color option func GetProductDetails(productId string) (models.TrendyolProductDetailModel, error) { var response models.TrendyolProductDetailResponse productDetailModel := models.TrendyolProductDetailModel{} // set linearVariants false to get variants url := "https://public.trendyol.com/discovery-web-productgw-service/api/productDetail/" + productId + "?storefrontId=1&culture=tr-TR&linearVariants=false" body, err := helper.SendRequest("GET", url, nil, "") if err != nil { return productDetailModel, err } err = json.Unmarshal(body, &response) if err != nil { helper.Error(err) return productDetailModel, err } productDetailModel = response.Result return productDetailModel, nil } // getProductDetailWithOptions returns JSON with variants func (l LinkParser) GetProductDetailWithOptions(productId, productGroupId int) (models.Product, error) { primaryProductDetail, err := GetProductDetails(strconv.Itoa(productId)) if err != nil { return models.Product{}, err } productDetailJSON := CreateJSONFromModel(primaryProductDetail) if productDetailJSON != nil { colorVariants, err := GetProductColorVariants(productGroupId) var colorVariantsJson []map[string]interface{} if err != nil { return models.Product{}, err } // get the color variant products for _, slicingAttribute := range colorVariants.Result.SlicingAttributes { for _, attribute := range slicingAttribute.Attributes { for _, content := range attribute.Contents { // Don't fetch primary product again. if content.ID != primaryProductDetail.ID { productVariantDetail, errGPD := GetProductDetails(strconv.Itoa(content.ID)) if errGPD == nil { productVariantJSON := CreateJSONFromModel(productVariantDetail) if productVariantJSON != nil { colorVariantsJson = append(colorVariantsJson, productVariantJSON) } } } } } // Color variant count if len(slicingAttribute.Attributes) > 0 { productDetailJSON["color_variant_count"] = len(slicingAttribute.Attributes) productDetailJSON["color_variants"] = colorVariantsJson } } } jsonString, _ := json.Marshal(productDetailJSON) // convert json to struct converted := models.Product{} json.Unmarshal(jsonString, &converted) return converted, nil } // getProductColorVariants returns color options of product func GetProductColorVariants(productGroupId int) (models.TrendyolProductVariantsResponse, error) { url := "https://public.trendyol.com/discovery-web-productgw-service/api/productGroup/" + strconv.Itoa(productGroupId) + "?storefrontId=1&culture=tr-TR" var response models.TrendyolProductVariantsResponse body, err := helper.SendRequest("GET", url, nil, "") if err != nil { return response, err } err = json.Unmarshal(body, &response) if err != nil { helper.Error(err) return response, err } return response, nil } // createJSONFromModel creates json from [trendyol.TrendyolProductDetailModel] and returns func CreateJSONFromModel(model models.TrendyolProductDetailModel) map[string]interface{} { json := map[string]interface{}{} // get weight from categories (Stored in helper array (RAM)) weight := helper.GetCategoryWeight(model.Category.BeautifiedName) productGroupId := strconv.Itoa(model.ProductGroupID) json["_id"] = productGroupId json["product_group_id"] = productGroupId json["vendor"] = "trendyol" json["sku"] = "p-" + strconv.Itoa(model.ID) json["product_number"] = strconv.Itoa(model.ID) json["product_code"] = model.ProductCode json["name"] = model.Name json["sellable"] = model.IsSellable json["favorite_count"] = model.FavoriteCount json["weight"] = weight json["name_with_product_code"] = model.NameWithProductCode json["url_key"] = "https://www.trendyol.com" + model.URL json["images"] = model.Images json["brand"] = model.Brand.Name json["cinsiyet"] = model.Gender.Name json["description"] = model.Description json["descriptions"] = model.ContentDescriptions json["short_description"] = model.Description // nested structure json["price"] = make(map[string]interface{}) json["price"].(map[string]interface{})["originalPrice"] = model.Price.OriginalPrice json["price"].(map[string]interface{})["sellingPrice"] = model.Price.SellingPrice json["price"].(map[string]interface{})["discountedPrice"] = model.Price.DiscountedPrice attrLen := len(model.Attributes) if attrLen != 0 { for i := 0; i < attrLen; i++ { attribute := model.Attributes[i] if attribute.Key.Name == "Renk" { json["color"] = attribute.Value.Name } } } // set categories with value 1 json["categories"] = []int{1} attributes := make([]map[string]string, 0) for _, attr := range model.Attributes { var re = regexp.MustCompile(`/[^A-Z0-9]/ig`) keyStr := re.ReplaceAllString(attr.Key.Name, `_`) key := strings.ToLower(keyStr) attribute := map[string]string{ key: attr.Value.Name, } attributes = append(attributes, attribute) } json["attributes"] = attributes var variants []models.Variant // if show variants, then it is configurable product. if model.ShowVariants { for i := 0; i < len(model.Variants); i++ { variant := model.Variants[i] if variant.Sellable { stockType := reflect.TypeOf(variant.Stock) if stockType == nil { variants = append(variants, variant) } else { // scrape is via link parse // we need to parse other variants // convert stock to int stock, ok := variant.Stock.(float64) if ok { if stock > 0 { variants = append(variants, variant) } } } } } for i := 0; i < len(model.AllVariants); i++ { singleVariant := model.AllVariants[i] // get the first variant for attribute info fv := variants[0] variant := models.Variant{ AttributeID: fv.AttributeID, // AttributeName: fv.AttributeName, // Sample: "Beden" AttributeType: fv.AttributeType, // Sample: "Size" AttributeValue: singleVariant.Value, Price: models.Price{ ProfitMargin: 0, DiscountedPrice: struct { Text string "json:\"text\"" Value float64 "json:\"value\"" }{ Text: fmt.Sprintf("%f", singleVariant.Price), Value: singleVariant.Price, }, SellingPrice: struct { Text string "json:\"text\"" Value float64 "json:\"value\"" }{ Text: fmt.Sprintf("%f", singleVariant.Price), Value: singleVariant.Price, }, OriginalPrice: struct { Text string "json:\"text\"" Value float64 "json:\"value\"" }{ Text: fmt.Sprintf("%f", singleVariant.Price), Value: singleVariant.Price, }, Currency: singleVariant.Currency, }, ItemNumber: singleVariant.ItemNumber, Sellable: singleVariant.InStock, } exists := helper.IsVariantsAdded(variants, variant.ItemNumber) if !exists { variants = append(variants, variant) } } for i := 0; i < len(model.AlternativeVariants); i++ { alternativeVariant := model.AlternativeVariants[i] if len(variants) > 0 { // get the first variant for attribute info fv := variants[0] variant := models.Variant{ AttributeID: fv.AttributeID, // AttributeName: fv.AttributeName, // Sample: "Beden" AttributeType: fv.AttributeType, // Sample: "Size" AttributeValue: alternativeVariant.AttributeValue, Price: alternativeVariant.Price, ItemNumber: alternativeVariant.ItemNumber, Sellable: alternativeVariant.Quantity > 0, } exists := helper.IsVariantsAdded(variants, variant.ItemNumber) if !exists { variants = append(variants, variant) } } } json["size_variants"] = variants } if model.ShowVariants && len(variants) == 0 { return nil } return json }