// CreateQuery creates a mgo.Query from a HTTP Request for a collection represented by endpointStruct.
//
// Examples:
//     mq := NewMongoQuery(People{}, db)
//     q, _ := mq.CreateQuery(req) // creates a query from the request for the people collection
//
//     mq.DisableParameters("name", "sort")
//     q, _ := mq.CreateQuery(req) // creates a query from the request for the people collection with the parameters "name" and "sort" disabled.
//
func (mq *MongoQuery) CreateQuery(req *http.Request) (*mgo.Query, error) {
	filterMap, err := mq.createQueryFilter(req)
	if err != nil {
		return nil, err
	}
	q := mq.dataBase.C(structName(mq.endPointStruct)).Find(filterMap)

	selectFields, err := mq.createFieldsMap(req)
	q.Select(selectFields)

	sortFields, err := mq.createSortFields(req)
	if err != nil {
		return nil, err
	}
	q.Sort(sortFields...)

	mq.page.Size, err = getUint(req, "limit", DefaultPageSize)
	if err != nil {
		return nil, merry.Wrap(err).WithHTTPCode(http.StatusBadRequest)
	}
	mq.page.Current, err = getUint(req, "page", 1)
	if err != nil {
		return nil, merry.Wrap(err).WithHTTPCode(http.StatusBadRequest)
	}
	if mq.page.Current == 0 {
		return nil, merry.Wrap(errors.New("page cannot be 0")).WithHTTPCode(http.StatusBadRequest)
	}
	q = q.Limit(int(mq.page.Size))
	q = q.Skip(int((mq.page.Current - 1) * mq.page.Size))
	return q, nil
}
Exemple #2
0
func apicall(u *neturl.URL) (*http.Response, error) {
	apikeys := []string{
		"sehm98r8ss3fddjfs8rwxur435xjav94",
		"trtrn4tdhzeruuypctm55a8m5td9z4ce",
		"cc7qrdaxn9h47ds39nmgzb9hnutxfsrr",
		"cbygdtw624xmfn2hr2sw3ayaeekg9cvy",
		"7zsnd2q7npg5bch8hacae7t28xskn8vr",
	}

	// if val, ok := options["apikey"]; ok {
	// 	apikeys = append([]string{to.String(val)}, apikeys...)
	// }

	rawQuery := u.RawQuery
	if len(rawQuery) > 0 {
		rawQuery += "&apikey="
	} else {
		rawQuery += "apikey="
	}

	var resp *http.Response
	var apierr error
	for _, apikey := range apikeys {
		u.RawQuery = rawQuery + apikey
		resp, apierr = http.Get(u.String())
		if apierr == nil {
			return resp, nil
		}
	}

	return nil, merry.Wrap(apierr)
}
func (mq *MongoQuery) createSortFields(req *http.Request) ([]string, error) {
	sortFields := []string{}
	if _sortField, ok := req.URL.Query()["sort"]; ok {
		for _, v := range _sortField {
			if _, ok := mq.supportedParameters[strings.Trim(v, "-")]; !ok {
				return nil, merry.Wrap(fmt.Errorf("unsupported field value: %s", v)).WithHTTPCode(http.StatusBadRequest)
			}
			sortFields = append(sortFields, v)
		}
	}
	return sortFields, nil
}
Exemple #4
0
// Get detailed codes from a character's armory page.
func wowarmory(options map[string]interface{}) (TMorphItems, error) {
	u, err := neturl.Parse(to.String(options["url"]))
	if err != nil {
		return nil, merry.Wrap(err)
	}

	parts := strings.Split(u.Path, "/")
	loc := -1
	for x := 0; x < len(parts); x++ {
		if parts[x] == "character" {
			loc = x + 1
			break
		}
	}

	if loc == -1 {
		return nil, errors.New("Could not parse battle.net URL")
	}

	// FIXME: this isn't exactly correct, because you can be in the US and want
	// to get the tmorph codes of a person in EU/China. So we need to probably
	// have settings to where the user of TMorphGen is.
	hostParts := strings.Split(u.Host, ".")
	if hostParts[0] == "cn" {
	} else if len(hostParts) == 2 {
		u.Host = "us.api." + strings.Join(hostParts, ".")
	} else {
		u.Host = hostParts[0] + ".api." + strings.Join(hostParts[1:], ".")
	}

	u.Scheme = "https"
	u.Path = fmt.Sprintf("/wow/character/%s/%s", parts[loc], parts[loc+1])
	u.RawQuery = "fields=items,appearance&locale=en_US"

	resp, err := apicall(u)
	if err != nil {
		return nil, merry.Wrap(err)
	}

	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, merry.Wrap(err)
	}
	resp.Body.Close()

	var tmorphItems TMorphItems
	var datam map[string]interface{}

	err = json.Unmarshal(data, &datam)
	if err != nil {
		return nil, merry.Wrap(err)
	}

	// get all armor, weapons, and enchants
	items := Map(datam["items"])
	for k, v := range items {
		if v, ok := v.(map[string]interface{}); ok {
			if canDisplayName(k) {
				id := to.Int64(v["id"])
				tooltipParams := Map(v["tooltipParams"])
				if !to.Bool(options["notmog"]) {
					transmogItem := tooltipParams["transmogItem"]
					if transmogItem != nil {
						id = to.Int64(transmogItem)
					}
				}

				tmorphItems = append(tmorphItems, &TMorphItem{
					Type: "item",
					Args: []int{nameSlotMap[k], int(id)},
				})

				// get enchants off the weapons
				if k == "mainHand" || k == "offHand" {
					if tooltipParams["enchant"] != nil {
						which := 1
						if k == "offHand" {
							which = 2
						}

						tmorphItems = append(tmorphItems, &TMorphItem{
							Type: "enchant",
							Args: []int{which, int(to.Int64(tooltipParams["enchant"]))},
						})
					}
				}
			}
		}
	}

	// set offhand to 0 if there is none.
	// TODO: maybe there should be defaults?
	if items["offHand"] == nil {
		tmorphItems = append(tmorphItems, &TMorphItem{
			Type: "item",
			Args: []int{nameSlotMap["offHand"], 0},
		})
	}

	// appearance stuff
	appearance := Map(datam["appearance"])
	for k, v := range appearance {
		if typ, ok := appearanceMap[k]; ok {
			tmorphItems = append(tmorphItems, &TMorphItem{
				Type: typ,
				Args: []int{int(to.Int64(v))},
			})
		}
	}
	tmorphItems = append(tmorphItems, &TMorphItem{
		Type: "race",
		Args: []int{int(to.Int64(datam["race"]))},
	}, &TMorphItem{
		Type: "gender",
		Args: []int{int(to.Int64(datam["gender"]))},
	})

	return tmorphItems, nil
}
Exemple #5
0
// Get the codes for the list of ids via the wow api.
func wowapi(ids []string) (TMorphItems, error) {
	var tmorphItems TMorphItems
	idslen := len(ids)
	errChan := make(chan error)
	doneChan := make(chan bool, idslen)

	for _, id := range ids {
		go func(id string) {
			defer func() {
				doneChan <- true
			}()

			contextUrl := ""

		REDO:
			// FIXME: using US api, ignoring user's location
			u, err := neturl.Parse("https://us.api.battle.net/wow/item/" + id + contextUrl)
			if err != nil {
				errChan <- err
				return
			}

			resp, err := apicall(u)
			if err != nil {
				errChan <- err
				return
			}

			data, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				errChan <- err
				return
			}
			resp.Body.Close()

			var datam map[string]interface{}
			err = json.Unmarshal(data, &datam)
			if err != nil {
				errChan <- err
				return
			}

			if _, ok := datam["inventoryType"]; !ok {
				if contexts, ok := datam["availableContexts"]; ok {
					ctxs := contexts.([]interface{})
					contextUrl = "/" + ctxs[0].(string)
					goto REDO
				}
			}

			slot := int(to.Int64(datam["inventoryType"]))
			if v, ok := slotMap[slot]; ok {
				slot = v
			}
			if canDisplaySlot(slot) {
				tmorphItems = append(tmorphItems, &TMorphItem{
					Type: "item",
					Args: []int{slot, int(to.Int64(id))},
				})
			}
		}(id)
	}

	count := 0
	for count < idslen {
		select {
		case err := <-errChan:
			return nil, merry.Wrap(err)
		case <-doneChan:
			count++
			if count >= idslen {
				return tmorphItems, nil
			}
		}
	}

	return nil, nil
}
func (mq *MongoQuery) createQueryFilter(req *http.Request) (map[string]interface{}, error) {
	filter := make(map[string]interface{})

	for parameterName, parameterValues := range req.URL.Query() {
		s := []interface{}{}
		if kind, ok := mq.supportedParameters[parameterName]; ok {
			// meta parameters are not filters
			if _, ok := validMetaParameters[parameterName]; ok {
				continue
			}
			switch kind {
			case reflect.Bool:
				for _, v := range parameterValues {
					b, err := strconv.ParseBool(v)
					if err != nil {
						return nil, merry.Wrap(err).WithHTTPCode(http.StatusBadRequest)
					}
					s = append(s, b)
				}
			case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
				for _, v := range parameterValues {
					i, err := strconv.Atoi(v)
					if err != nil {
						return nil, merry.Wrap(err).WithHTTPCode(http.StatusBadRequest)
					}
					s = append(s, i)
				}
			case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
				for _, v := range parameterValues {
					i, err := strconv.ParseUint(v, 10, 0)
					if err != nil {
						return nil, merry.Wrap(err).WithHTTPCode(http.StatusBadRequest)
					}
					s = append(s, uint(i))
				}
			case reflect.Float32, reflect.Float64:
				for _, v := range parameterValues {
					f, err := strconv.ParseFloat(v, 64)
					if err != nil {
						return nil, merry.Wrap(err).WithHTTPCode(http.StatusBadRequest)
					}
					s = append(s, f)
				}
			case reflect.String:
				if len(parameterValues) == 1 {
					if bson.IsObjectIdHex(parameterValues[0]) {
						s = []interface{}{bson.ObjectIdHex(parameterValues[0])}
					} else {
						s = []interface{}{bson.RegEx{Pattern: parameterValues[0], Options: ""}}
					}
				} else {
					for _, v := range parameterValues {
						if bson.IsObjectIdHex(v) {
							s = append(s, bson.ObjectIdHex(v))
						} else {
							s = append(s, v)
						}
					}
				}
			default:
				return nil, merry.Wrap(fmt.Errorf("reflection kind '%s' is not supported", kind)).WithHTTPCode(http.StatusBadRequest)
			}
		} else {
			return nil, merry.Wrap(fmt.Errorf("parameter '%s' is not supported", parameterName)).WithHTTPCode(http.StatusBadRequest)
		}
		if len(s) == 1 {
			filter[parameterName] = s[0]
		} else {
			filter[parameterName] = map[string]interface{}{
				"$in": s,
			}
		}
	}
	return filter, nil
}