Beispiel #1
0
// ChallengeByKey loads a challenge by key.
func ChallengeByKey(ctx context.Context, w http.ResponseWriter, r *http.Request) (status int, err error) {
	if r.Method != "GET" {
		return http.StatusMethodNotAllowed, nil
	}

	p, ok := passenger.FromContext(ctx)
	if !ok {
		return http.StatusUnauthorized, nil
	}

	key, err := datastore.DecodeKey(mux.Vars(r)["key"])
	if err != nil {
		return http.StatusInternalServerError, err
	}

	var challenge model.Challenge

	err = datastore.Get(ctx, key, &challenge)
	if err != nil {
		return http.StatusInternalServerError, err
	}

	e := json.NewEncoder(w)
	if parent := p.UserKey.Parent(); parent == nil {
		// The current user is a coder so we must also create a result.
		e.Encode(challenge.Key(key))
	} else {
		// TODO(pbochis): If a company representativemakes the request
		// we also include Tasks in the response.
		e.Encode(challenge.Key(key))
	}

	return http.StatusOK, nil
}
Beispiel #2
0
func GetCompanyByUser(ctx context.Context, w http.ResponseWriter, r *http.Request) (int, error) {
	if r.Method != "GET" {
		return http.StatusMethodNotAllowed, nil
	}

	p, ok := passenger.FromContext(ctx)
	if !ok {
		return http.StatusUnauthorized, nil
	}

	var u model.User
	if err := datastore.Get(ctx, p.User, &u); err != nil {
		return http.StatusInternalServerError, nil
	}

	if u.Company == nil {
		return http.StatusUnauthorized, nil
	}

	// The account is associated with a company, so we return it.
	var company model.Company
	if err := datastore.Get(ctx, u.Company, &company); err != nil {
		return http.StatusInternalServerError, err
	}

	json.NewEncoder(w).Encode(company.Key(u.Company))
	return http.StatusOK, nil
}
Beispiel #3
0
func Tasks(ctx context.Context, w http.ResponseWriter, r *http.Request) (status int, err error) {
	p, ok := passenger.FromContext(ctx)
	if !ok {
		return http.StatusUnauthorized, nil
	}

	if r.Method != "GET" {
		return http.StatusMethodNotAllowed, nil
	}

	// User is a coder
	if p.UserKey.Parent() == nil {
		return http.StatusUnauthorized, nil
	}

	var codeTasks model.CodeTasks
	codeTaskKeys, err := model.NewQueryForCodeTask().
		GetAll(ctx, &codeTasks)

	if err != nil {
		return http.StatusInternalServerError, err
	}

	tasks := make([]model.KeyedTask, len(codeTasks))
	for i := range codeTasks {
		tasks[i] = model.KeyedTask{
			Task: &codeTasks[i].Task,
			Key:  codeTaskKeys[i],
		}
	}

	json.NewEncoder(w).Encode(tasks)
	return http.StatusOK, nil
}
Beispiel #4
0
func Tasks(ctx context.Context, w http.ResponseWriter, r *http.Request) (status int, err error) {
	p, ok := passenger.FromContext(ctx)
	if !ok {
		return http.StatusUnauthorized, nil
	}

	var u model.User
	if err = datastore.Get(ctx, p.User, &u); err != nil {
		return http.StatusInternalServerError, err
	}

	// User is a coder
	if u.Company == nil {
		return http.StatusUnauthorized, nil
	}

	switch r.Method {
	case "GET":
		return getAllTasks(ctx, w, r)
	case "POST":
		return createTask(ctx, w, r)
	default:
		return http.StatusMethodNotAllowed, nil
	}
}
Beispiel #5
0
// AccessTokens will create new AccessTokens for the user.
func AccessTokens(ctx context.Context, w http.ResponseWriter, r *http.Request) (status int, err error) {
	if r.Method != "POST" {
		return http.StatusMethodNotAllowed, nil
	}

	p, ok := passenger.FromContext(ctx)
	if !ok {
		return http.StatusUnauthorized, nil
	}

	var body model.AccessToken

	if err = json.NewDecoder(r.Body).Decode(&body); err != nil {
		return http.StatusBadRequest, err
	}

	value, err := p.IssueToken(ctx, &body)

	var result = struct {
		Value       string
		Creation    time.Time
		Expiry      time.Time
		Description string
	}{
		Value:       value,
		Creation:    body.Creation,
		Expiry:      body.Expiry,
		Description: body.Description,
	}

	json.NewEncoder(w).Encode(result)
	return
}
Beispiel #6
0
// PostSubmission creates a new submission.
func PostSubmission(ctx context.Context, w http.ResponseWriter, r *http.Request) (status int, err error) {
	p, ok := passenger.FromContext(ctx)
	if !ok {
		return http.StatusUnauthorized, nil
	}
	if r.Method != "POST" {
		return http.StatusMethodNotAllowed, nil
	}

	resultKey, err := datastore.DecodeKey(mux.Vars(r)["resultKey"])

	if !util.HasParent(p.UserKey, resultKey) {
		return http.StatusBadRequest, errors.New("cannot submit answer for other users")
	}

	taskKey, err := datastore.DecodeKey(mux.Vars(r)["taskKey"])
	// Note: When more task kinds are added, see controllers.CreateFinalResult.
	switch taskKey.Kind() {
	case model.CodeTaskKind:
		return runner.HandleCodeSubmission(ctx, w, r, resultKey, taskKey)
	// TODO(victorbalan, flowlo): Use correct kind when possible.
	case "QuestionTask":
		return http.StatusInternalServerError, errors.New("question submissions are not yet implemented")
	default:
		return http.StatusBadRequest, errors.New("Unknown submission kind.")
	}
}
Beispiel #7
0
func Tasks(ctx context.Context, w http.ResponseWriter, r *http.Request) (status int, err error) {
	if r.Method != "GET" {
		return http.StatusMethodNotAllowed, nil
	}

	p, ok := passenger.FromContext(ctx)
	if !ok {
		return http.StatusUnauthorized, nil
	}

	var u model.User
	if err = datastore.Get(ctx, p.User, &u); err != nil {
		return http.StatusInternalServerError, nil
	}

	// User is a coder
	if u.Company == nil {
		return http.StatusUnauthorized, nil
	}

	var tasks model.Tasks
	taskKeys, err := model.NewQueryForTask().
		GetAll(ctx, &tasks)

	if err != nil {
		return http.StatusInternalServerError, err
	}

	json.NewEncoder(w).Encode(tasks.Key(taskKeys))
	return http.StatusOK, nil
}
Beispiel #8
0
func putCookie(ctx context.Context, w http.ResponseWriter, r *http.Request) (int, error) {
	p, ok := passenger.FromContext(ctx)
	if !ok {
		return http.StatusUnauthorized, nil
	}

	token := &model.Token{
		Description: "Login from " + r.RemoteAddr,
	}

	value, err := p.IssueToken(ctx, token)
	if err != nil {
		return http.StatusInternalServerError, err
	}

	http.SetCookie(w, &http.Cookie{
		Name:     "token",
		Value:    value,
		Secure:   !appengine.IsDevAppServer(),
		HttpOnly: true,
		Expires:  token.Expiry,
	})

	w.Write([]byte("OK"))
	return http.StatusOK, nil
}
Beispiel #9
0
func GetResult(ctx context.Context, w http.ResponseWriter, r *http.Request) (int, error) {
	if !util.CheckMethod(r, "GET") {
		return http.StatusMethodNotAllowed, nil
	}

	resultKey, err := datastore.DecodeKey(mux.Vars(r)["resultKey"])
	if err != nil {
		return http.StatusBadRequest, err
	}

	var result model.Result
	if err := datastore.Get(ctx, resultKey, &result); err != nil {
		return http.StatusInternalServerError, nil
	}

	p, ok := passenger.FromContext(ctx)
	if !ok {
		return http.StatusUnauthorized, nil
	}

	if p.UserKey.Parent() != nil {
		json.NewEncoder(w).Encode(result.Key(resultKey))
		return http.StatusOK, nil
	}

	if !util.HasParent(resultKey, p.UserKey) {
		return http.StatusUnauthorized, nil
	}
	return createFinalResult(ctx, w, resultKey, result)
}
Beispiel #10
0
// GetChallengesForCompany queries all the challenges defined  by a company.
func GetChallengesForCompany(ctx context.Context, w http.ResponseWriter, r *http.Request) (status int, err error) {
	if r.Method != "GET" {
		return http.StatusMethodNotAllowed, nil
	}

	_, ok := passenger.FromContext(ctx)
	if !ok {
		return http.StatusUnauthorized, nil
	}

	key, err := datastore.DecodeKey(mux.Vars(r)["key"])
	if err != nil {
		return http.StatusInternalServerError, err
	}

	var challenges model.Challenges

	keys, err := model.NewQueryForChallenge().
		Ancestor(key).
		GetAll(ctx, &challenges)

	if err != nil {
		return http.StatusInternalServerError, err
	}

	json.NewEncoder(w).Encode(challenges.Key(keys))
	return http.StatusOK, nil
}
Beispiel #11
0
func GetProfileForUser(ctx context.Context, w http.ResponseWriter, r *http.Request) (status int, err error) {
	_, ok := passenger.FromContext(ctx)
	if !ok {
		return http.StatusUnauthorized, nil
	}

	var userKey *datastore.Key
	if userKey, err = datastore.DecodeKey(mux.Vars(r)["key"]); err != nil {
		return http.StatusInternalServerError, err
	}

	var profiles []model.Profile
	keys, err := model.NewQueryForProfile().
		Ancestor(userKey).
		Limit(1).
		GetAll(ctx, &profiles)
	if err != nil {
		return http.StatusInternalServerError, err
	}
	if len(keys) != 1 {
		return http.StatusNotFound, nil
	}
	json.NewEncoder(w).Encode(profiles[0].Key(keys[0]))
	return
}
Beispiel #12
0
func GetTestResultsForSubmission(ctx context.Context, w http.ResponseWriter, r *http.Request) (status int, err error) {
	if r.Method != "GET" {
		return http.StatusMethodNotAllowed, nil
	}

	// TODO(victorbalan): Check if user is company or if user is parent of result
	// else return http.StatusUnauthorized
	_, ok := passenger.FromContext(ctx)
	if !ok {
		return http.StatusUnauthorized, nil
	}

	submissionKey, err := datastore.DecodeKey(mux.Vars(r)["key"])
	if err != nil {
		return http.StatusNotFound, err
	}

	keys, err := datastore.NewQuery("").
		Ancestor(submissionKey).
		Filter("__key__ >", submissionKey).
		KeysOnly().
		GetAll(ctx, nil)
	if err != nil {
		return http.StatusInternalServerError, err
	}
	if len(keys) == 0 {
		json.NewEncoder(w).Encode([]string{})
		return http.StatusOK, nil
	}

	switch keys[0].Kind() {
	case model.JunitTestResultKind:
		var results model.JunitTestResults
		_, err = datastore.NewQuery(keys[0].Kind()).
			Ancestor(submissionKey).
			GetAll(ctx, &results)
		if err != nil {
			return http.StatusInternalServerError, err
		}
		json.NewEncoder(w).Encode(results)
	case model.DiffTestResultKind:
		var results model.DiffTestResults
		_, err = datastore.NewQuery(keys[0].Kind()).
			Ancestor(submissionKey).
			GetAll(ctx, &results)
		if err != nil {
			return http.StatusInternalServerError, err
		}
		json.NewEncoder(w).Encode(results)
	default:
		w.Write([]byte("[]"))
	}
	return http.StatusOK, nil
}
Beispiel #13
0
// CreateChallenge will put a new entity of kind Challenge to Datastore.
func CreateChallenge(ctx context.Context, w http.ResponseWriter, r *http.Request) (status int, err error) {
	if r.Method != "POST" {
		return http.StatusMethodNotAllowed, nil
	}

	p, ok := passenger.FromContext(ctx)
	if !ok {
		return http.StatusUnauthorized, nil
	}

	var u model.User
	if err := datastore.Get(ctx, p.User, &u); err != nil {
		return http.StatusInternalServerError, err
	}

	if u.Company == nil {
		return http.StatusUnauthorized, nil
	}

	var body = struct {
		model.Assignment
		Tasks []string
	}{}

	if err = json.NewDecoder(r.Body).Decode(&body); err != nil {
		return http.StatusBadRequest, err
	}

	keys := make([]*datastore.Key, len(body.Tasks))
	for i := range body.Tasks {
		key, err := datastore.DecodeKey(body.Tasks[i])
		if err != nil {
			return http.StatusInternalServerError, err
		}
		keys[i] = key
	}

	challenge := model.Challenge{
		Assignment: body.Assignment,
		Resulter:   int64(logic.Average),
		Tasks:      keys,
	}

	key, err := challenge.PutWithParent(ctx, u.Company)
	if err != nil {
		return http.StatusInternalServerError, err
	}

	json.NewEncoder(w).Encode(challenge.Key(key))
	return http.StatusOK, nil
}
Beispiel #14
0
func User(ctx context.Context, w http.ResponseWriter, r *http.Request) (status int, err error) {
	p, ok := passenger.FromContext(ctx)
	if !ok {
		return http.StatusUnauthorized, nil
	}
	switch r.Method {
	case "POST":
		return createUser(ctx, w, r)
	case "GET":
		return getUsers(p, ctx, w, r)
	default:
		return http.StatusMethodNotAllowed, nil
	}
}
Beispiel #15
0
func GetChallengesForProfile(ctx context.Context, w http.ResponseWriter, r *http.Request) (status int, err error) {
	if r.Method != "GET" {
		return http.StatusMethodNotAllowed, nil
	}

	_, ok := passenger.FromContext(ctx)
	if !ok {
		return http.StatusUnauthorized, nil
	}

	var profileKey *datastore.Key
	if profileKey, err = datastore.DecodeKey(mux.Vars(r)["key"]); err != nil {
		return http.StatusInternalServerError, err
	}

	q := model.NewQueryForResult().
		Ancestor(profileKey)
	if finished := r.URL.Query()["finished"]; len(finished) > 0 && finished[0] == "true" {
		q = q.Filter("Finished >", time.Time{})
	}
	if order := r.URL.Query()["order"]; len(order) > 0 && order[0] != "" {
		q = q.Order(order[0])
	}

	if limitQuery := r.URL.Query()["limit"]; len(limitQuery) > 0 {
		if limit, err := strconv.Atoi(limitQuery[0]); err != nil {
			return http.StatusInternalServerError, err
		} else {
			q = q.Limit(limit)
		}
	}

	var results model.Results
	if _, err = q.GetAll(ctx, &results); err != nil {
		return http.StatusInternalServerError, err
	}

	challengeKeys := make([]*datastore.Key, len(results))
	for i, val := range results {
		challengeKeys[i] = val.Challenge
	}

	challenges := make(model.Challenges, len(challengeKeys))
	if err = datastore.GetMulti(ctx, challengeKeys, challenges); err != nil {
		return http.StatusInternalServerError, err
	}

	json.NewEncoder(w).Encode(challenges.Key(challengeKeys))
	return
}
Beispiel #16
0
func GetResult(ctx context.Context, w http.ResponseWriter, r *http.Request) (int, error) {
	if r.Method != "GET" {
		return http.StatusMethodNotAllowed, nil
	}

	resultKey, err := datastore.DecodeKey(mux.Vars(r)["resultKey"])
	if err != nil {
		return http.StatusBadRequest, err
	}

	var result model.Result
	if err := datastore.Get(ctx, resultKey, &result); err != nil {
		return http.StatusInternalServerError, nil
	}

	p, ok := passenger.FromContext(ctx)
	if !ok {
		return http.StatusUnauthorized, nil
	}

	var u model.User
	if err = datastore.Get(ctx, p.User, &u); err != nil {
		return http.StatusInternalServerError, nil
	}

	if u.Company == nil && !util.HasParent(p.User, resultKey) {
		return http.StatusUnauthorized, nil
	}

	if result.Finished.Equal(time.Time{}) {
		if util.HasParent(p.User, resultKey) {
			return createFinalResult(ctx, w, *result.Key(resultKey))
		}
		var challenge model.Challenge
		if err := datastore.Get(ctx, result.Challenge, &challenge); err != nil {
			return http.StatusInternalServerError, err
		}
		if u.Company != nil && result.Started.Add(challenge.Duration).Before(time.Now()) {
			return createFinalResult(ctx, w, *result.Key(resultKey))
		}
	}

	json.NewEncoder(w).Encode(result.Key(resultKey))
	return http.StatusOK, nil
}
Beispiel #17
0
// FinalSubmission makes the last submission final.
func FinalSubmission(ctx context.Context, w http.ResponseWriter, r *http.Request) (status int, err error) {
	if r.Method != "POST" {
		return http.StatusMethodNotAllowed, nil
	}

	p, ok := passenger.FromContext(ctx)
	if !ok {
		return http.StatusUnauthorized, nil
	}

	var resultKey *datastore.Key
	if resultKey, err = datastore.DecodeKey(mux.Vars(r)["resultKey"]); err != nil {
		return http.StatusInternalServerError, err
	}

	if !util.HasParent(p.User, resultKey) {
		return http.StatusBadRequest, errors.New("cannot submit answer for other users")
	}

	var index int
	if index, err = strconv.Atoi(mux.Vars(r)["index"]); err != nil {
		return http.StatusInternalServerError, err
	}

	if len(r.URL.Query()["submissionKey"]) == 0 {
		return http.StatusOK, nil
	}
	var submissionKey *datastore.Key
	if submissionKey, err = datastore.DecodeKey(r.URL.Query()["submissionKey"][0]); err != nil {
		return http.StatusInternalServerError, err
	}

	var result model.Result
	if err = datastore.Get(ctx, resultKey, &result); err != nil {
		return http.StatusInternalServerError, err
	}

	result.FinalSubmissions[index] = submissionKey

	if _, err = result.Put(ctx, resultKey); err != nil {
		return http.StatusInternalServerError, err
	}
	w.Write([]byte("OK"))
	return
}
Beispiel #18
0
func WhoAmI(ctx context.Context, w http.ResponseWriter, r *http.Request) (int, error) {
	if r.Method != "GET" {
		return http.StatusMethodNotAllowed, nil
	}

	p, ok := passenger.FromContext(ctx)
	if !ok {
		return http.StatusUnauthorized, nil
	}

	var user model.User
	if err := datastore.Get(ctx, p.UserKey, &user); err != nil {
		return http.StatusInternalServerError, err
	}

	json.NewEncoder(w).Encode(user.Key(p.UserKey))
	return http.StatusOK, nil
}
Beispiel #19
0
func GetUser(ctx context.Context, w http.ResponseWriter, r *http.Request) (status int, err error) {
	_, ok := passenger.FromContext(ctx)
	if !ok {
		return http.StatusUnauthorized, nil
	}

	var userKey *datastore.Key
	if userKey, err = datastore.DecodeKey(mux.Vars(r)["key"]); err != nil {
		return http.StatusInternalServerError, err
	}

	var user model.User
	if err = datastore.Get(ctx, userKey, &user); err != nil {
		return http.StatusInternalServerError, err
	}

	json.NewEncoder(w).Encode(user.Key(userKey))
	return
}
Beispiel #20
0
func GetCompanyByUser(ctx context.Context, w http.ResponseWriter, r *http.Request) (int, error) {
	if !util.CheckMethod(r, "GET") {
		return http.StatusMethodNotAllowed, nil
	}
	p, ok := passenger.FromContext(ctx)

	if !ok {
		return http.StatusUnauthorized, nil
	}
	key := p.UserKey.Parent()
	if key == nil {
		return http.StatusUnauthorized, nil
	}
	// The account is associated with a company, so we return it.
	var company model.Company
	if err := datastore.Get(ctx, key, &company); err != nil {
		return http.StatusInternalServerError, err
	}
	json.NewEncoder(w).Encode(company.Key(key))
	return http.StatusOK, nil
}
Beispiel #21
0
func GetSubmissionByKey(ctx context.Context, w http.ResponseWriter, r *http.Request) (status int, err error) {
	if r.Method != "GET" {
		return http.StatusMethodNotAllowed, nil
	}

	// TODO(victorbalan): Check if user is company or if user is parent of result
	// else return http.StatusUnauthorized
	_, ok := passenger.FromContext(ctx)
	if !ok {
		return http.StatusUnauthorized, nil
	}

	submissionKey, err := datastore.DecodeKey(mux.Vars(r)["key"])
	if err != nil {
		return http.StatusNotFound, err
	}

	var submission model.Submission
	if err = datastore.Get(ctx, submissionKey, &submission); err != nil {
		return
	}

	codeFilesURLs, err := util.ExposeMultiURL(ctx, submission.Code.Bucket, submission.Code.Name)
	if err != nil {
		return http.StatusInternalServerError, err
	}

	s := struct {
		Time     time.Time
		CodeURLs []string
		Language string
	}{
		Time:     submission.Time,
		Language: submission.Language,
		CodeURLs: codeFilesURLs,
	}

	json.NewEncoder(w).Encode(s)
	return http.StatusOK, nil
}
Beispiel #22
0
func TestsByTaskKey(ctx context.Context, w http.ResponseWriter, r *http.Request) (status int, err error) {
	_, ok := passenger.FromContext(ctx)
	if !ok {
		return http.StatusUnauthorized, nil
	}

	if r.Method != "GET" {
		return http.StatusMethodNotAllowed, nil
	}

	taskKey, err := datastore.DecodeKey(mux.Vars(r)["key"])
	if err != nil {
		return http.StatusInternalServerError, err
	}

	var tests model.Tests
	var testKeys []*datastore.Key
	if testKeys, err = model.NewQueryForTest().Ancestor(taskKey).GetAll(ctx, &tests); err != nil {
		return http.StatusInternalServerError, err
	}

	json.NewEncoder(w).Encode(tests.Key(testKeys))
	return http.StatusOK, nil
}
Beispiel #23
0
func GetSubmissionsForResult(ctx context.Context, w http.ResponseWriter, r *http.Request) (status int, err error) {
	if r.Method != "GET" {
		return http.StatusMethodNotAllowed, nil
	}

	// TODO(victorbalan): Check if user is company or if user is parent of result
	// else return http.StatusUnauthorized
	_, ok := passenger.FromContext(ctx)
	if !ok {
		return http.StatusUnauthorized, nil
	}

	resultKey, err := datastore.DecodeKey(mux.Vars(r)["resultKey"])
	if err != nil {
		return http.StatusNotFound, err
	}

	taskKey, err := datastore.DecodeKey(mux.Vars(r)["taskKey"])
	if err != nil {
		return http.StatusNotFound, err
	}

	var submissions model.Submissions
	var keys []*datastore.Key
	keys, err = model.NewQueryForSubmission().
		Ancestor(resultKey).
		Filter("Task =", taskKey).
		Order("Time").
		GetAll(ctx, &submissions)
	if err != nil {
		return http.StatusInternalServerError, err
	}

	json.NewEncoder(w).Encode(submissions.Key(keys))
	return http.StatusOK, nil
}
Beispiel #24
0
// TaskByKey loads a task by key.
func TaskByKey(ctx context.Context, w http.ResponseWriter, r *http.Request) (status int, err error) {
	p, ok := passenger.FromContext(ctx)
	if !ok {
		return http.StatusUnauthorized, nil
	}

	if r.Method != "GET" {
		return http.StatusMethodNotAllowed, nil
	}

	taskKey, err := datastore.DecodeKey(mux.Vars(r)["key"])
	if err != nil {
		return http.StatusInternalServerError, err
	}

	// User is a coder
	if p.UserKey.Parent() == nil {
		rk, err := datastore.DecodeKey(r.URL.Query()["result"][0])
		if err != nil {
			return http.StatusInternalServerError, err
		}

		if !util.HasParent(p.UserKey, rk) {
			return http.StatusUnauthorized, nil
		}

		var result model.Result
		if err = datastore.Get(ctx, rk, &result); err != nil {
			return http.StatusInternalServerError, err
		}

		var challenge model.Challenge
		if err = datastore.Get(ctx, result.Challenge, &challenge); err != nil {
			return http.StatusInternalServerError, err
		}

		emptyTime := time.Time{}
		updateResult := false
		for i, val := range challenge.Tasks {
			if taskKey.Equal(val) && result.StartTimes[i] == emptyTime {
				result.StartTimes[i] = time.Now()
				updateResult = true
				break
			}
		}
		if updateResult {
			if _, err = result.Save(ctx, rk); err != nil {
				return http.StatusInternalServerError, err
			}
		}
	}

	switch taskKey.Kind() {
	case model.CodeTaskKind:
		var task model.CodeTask
		if err = datastore.Get(ctx, taskKey, &task); err != nil {
			return http.StatusInternalServerError, err
		}
		json.NewEncoder(w).Encode(task.Key(taskKey))
		return http.StatusOK, nil
	default:
		return http.StatusInternalServerError, errors.New("Not implemented")
	}
}
Beispiel #25
0
// PostSubmission creates a new submission.
func PostSubmission(ctx context.Context, w http.ResponseWriter, r *http.Request) (status int, err error) {
	if r.Method != "POST" {
		return http.StatusMethodNotAllowed, nil
	}

	var body = struct {
		Code     string
		Language string
	}{}

	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		return http.StatusBadRequest, err
	}

	p, ok := passenger.FromContext(ctx)
	if !ok {
		return http.StatusUnauthorized, nil
	}

	resultKey, err := datastore.DecodeKey(mux.Vars(r)["resultKey"])
	if err != nil {
		return http.StatusNotFound, err
	}

	if !util.HasParent(p.User, resultKey) {
		return http.StatusBadRequest, errors.New("cannot submit answer for other users")
	}

	taskKey, err := datastore.DecodeKey(mux.Vars(r)["taskKey"])
	if err != nil {
		return http.StatusNotFound, err
	}

	var task model.Task
	if err = datastore.Get(ctx, taskKey, &task); err != nil {
		return http.StatusInternalServerError, err
	}

	// Furthermore, the name of the GCS object is derived from the of the
	// encapsulating Submission. To avoid race conditions, allocate an ID.
	low, _, err := datastore.AllocateIDs(ctx, model.SubmissionKind, resultKey, 1)
	if err != nil {
		return http.StatusInternalServerError, err
	}

	submissionKey := datastore.NewKey(ctx, model.SubmissionKind, "", low, resultKey)

	submission := model.Submission{
		Task: taskKey,
		Time: time.Now(),
	}

	if body.Code != "" {
		submission.Code, err = store(ctx, submissionKey, body.Code, body.Language)
		if err != nil {
			return http.StatusInternalServerError, err
		}
		submission.Language = body.Language
	}

	// Set the submission in stone.
	if _, err = datastore.Put(ctx, submissionKey, &submission); err != nil {
		return http.StatusInternalServerError, err
	}

	var tests model.Tests
	_, err = model.NewQueryForTest().
		Ancestor(taskKey).
		GetAll(ctx, &tests)

	if err != nil {
		return http.StatusInternalServerError, err
	}

	for _, t := range tests {
		if err := test.Tester(t.Tester).Call(ctx, t.Params, *submission.Key(submissionKey)); err != nil {
			log.Warningf(ctx, "%s", err)
			continue
		}
	}

	// TODO(flowlo): Return something meaningful.

	return http.StatusOK, nil
}
Beispiel #26
0
// PostSubmission creates a new submission.
func PostSubmission(ctx context.Context, w http.ResponseWriter, r *http.Request) (status int, err error) {
	if r.Method != "POST" {
		return http.StatusMethodNotAllowed, nil
	}

	p, ok := passenger.FromContext(ctx)
	if !ok {
		return http.StatusUnauthorized, nil
	}

	mediaType, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
	if err != nil {
		return http.StatusBadRequest, err
	}
	if !strings.HasPrefix(mediaType, "multipart/") {
		return http.StatusUnsupportedMediaType, nil
	}

	resultKey, err := datastore.DecodeKey(mux.Vars(r)["resultKey"])
	if err != nil {
		return http.StatusNotFound, err
	}

	if !util.HasParent(p.User, resultKey) {
		return http.StatusBadRequest, errors.New("cannot submit answer for other users")
	}

	taskKey, err := datastore.DecodeKey(mux.Vars(r)["taskKey"])
	if err != nil {
		return http.StatusNotFound, err
	}

	if err := r.ParseMultipartForm(16 << 20); err != nil {
		return http.StatusBadRequest, err
	}

	files, ok := r.MultipartForm.File["files"]
	if !ok {
		return http.StatusBadRequest, errors.New("missing files")
	}

	var task model.Task
	if err = datastore.Get(ctx, taskKey, &task); err != nil {
		return http.StatusNotFound, err
	}

	// Furthermore, the name of the GCS object is derived from the of the
	// encapsulating Submission. To avoid race conditions, allocate an ID.
	low, _, err := datastore.AllocateIDs(ctx, model.SubmissionKind, resultKey, 1)
	if err != nil {
		return http.StatusInternalServerError, err
	}

	submissionKey := datastore.NewKey(ctx, model.SubmissionKind, "", low, resultKey)
	storedCode := model.StoredObject{
		Bucket: util.SubmissionBucket(),
		Name:   nameObject(submissionKey) + "/Code/",
	}
	submission := model.Submission{
		Task:     taskKey,
		Time:     time.Now(),
		Language: detectLanguage(files),
		Code:     storedCode,
	}

	if _, err = datastore.Put(ctx, submissionKey, &submission); err != nil {
		return http.StatusInternalServerError, err
	}

	var tests model.Tests
	testKeys, err := model.NewQueryForTest().
		Ancestor(taskKey).
		GetAll(ctx, &tests)
	if err != nil {
		return http.StatusInternalServerError, err
	}

	prrs, pwrs := multiPipe(len(tests))

	go maketar(pwrs, files)

	for i, t := range tests {
		go func(i int, t model.Test) {
			if err := test.Tester(t.Tester).Call(ctx, *t.Key(testKeys[i]), *submission.Key(submissionKey), prrs[i]); err != nil {
				log.Warningf(ctx, "%s", err)
			}
		}(i, t)
	}

	if err := upload(util.CloudContext(ctx), storedCode.Bucket, storedCode.Name, files); err != nil {
		return http.StatusInternalServerError, err
	}

	return http.StatusOK, nil
}
Beispiel #27
0
// TaskByKey loads a task by key.
func TaskByKey(ctx context.Context, w http.ResponseWriter, r *http.Request) (status int, err error) {
	p, ok := passenger.FromContext(ctx)
	if !ok {
		return http.StatusUnauthorized, nil
	}

	if r.Method != "GET" {
		return http.StatusMethodNotAllowed, nil
	}

	taskKey, err := datastore.DecodeKey(mux.Vars(r)["key"])
	if err != nil {
		return http.StatusInternalServerError, err
	}

	var u model.User
	if err = datastore.Get(ctx, p.User, &u); err != nil {
		return http.StatusInternalServerError, nil
	}

	if len(r.URL.Query()["result"]) > 0 {
		rk, err := datastore.DecodeKey(r.URL.Query()["result"][0])
		if err != nil {
			return http.StatusInternalServerError, err
		}

		if util.HasParent(p.User, rk) {
			var result model.Result
			if err = datastore.Get(ctx, rk, &result); err != nil {
				return http.StatusInternalServerError, err
			}

			var challenge model.Challenge
			if err = datastore.Get(ctx, result.Challenge, &challenge); err != nil {
				return http.StatusInternalServerError, err
			}

			emptyTime := time.Time{}
			updateResult := false
			for i, val := range challenge.Tasks {
				if taskKey.Equal(val) {
					if result.StartTimes[i].Equal(emptyTime) {
						result.StartTimes[i] = time.Now()
						updateResult = true
						break
					}
				}
			}
			if updateResult {
				if _, err = result.Put(ctx, rk); err != nil {
					return http.StatusInternalServerError, err
				}
			}
		}
	}

	var task model.Task
	if err = datastore.Get(ctx, taskKey, &task); err != nil {
		return http.StatusInternalServerError, err
	}
	json.NewEncoder(w).Encode(task.Key(taskKey))
	return http.StatusOK, nil
}
Beispiel #28
0
// CreateResult saves a new result when a coder starts a challenge.
func CreateResult(ctx context.Context, w http.ResponseWriter, r *http.Request) (int, error) {
	if r.Method != "POST" {
		return http.StatusMethodNotAllowed, nil
	}

	var body = struct {
		ChallengeKey string
	}{}

	p, ok := passenger.FromContext(ctx)

	if !ok {
		return http.StatusUnauthorized, nil
	}

	var profiles model.Profiles
	keys, err := model.NewQueryForProfile().
		Ancestor(p.User).
		GetAll(ctx, &profiles)

	if len(keys) != 1 {
		return http.StatusInternalServerError, errors.New("Profile not found")
	}

	err = json.NewDecoder(r.Body).Decode(&body)

	if err != nil {
		return http.StatusBadRequest, err
	}

	key, err := datastore.DecodeKey(body.ChallengeKey)

	if err != nil {
		return http.StatusBadRequest, err
	}

	var results []model.Result

	resultKeys, err := model.NewQueryForResult().
		Ancestor(keys[0]).
		Filter("Challenge = ", key).
		Limit(1).
		GetAll(ctx, &results)

	if err != nil {
		return http.StatusInternalServerError, err
	}

	if len(resultKeys) == 1 {
		json.NewEncoder(w).Encode(results[0].Key(resultKeys[0]))
		return http.StatusOK, nil
	}

	var challenge model.Challenge
	if err = datastore.Get(ctx, key, &challenge); err != nil {
		return http.StatusInternalServerError, err
	}

	result := model.Result{
		Challenge:        key,
		StartTimes:       make([]time.Time, len(challenge.Tasks)),
		FinalSubmissions: make([]*datastore.Key, len(challenge.Tasks)),
		Started:          time.Now(),
	}
	key, err = result.PutWithParent(ctx, keys[0])
	if err != nil {
		return http.StatusInternalServerError, err
	}

	json.NewEncoder(w).Encode(result.Key(key))
	return http.StatusOK, nil
}
Beispiel #29
0
// Invitation handles the creation of a new invitation and sends an e-mail to
// the user.
func Invitation(ctx context.Context, w http.ResponseWriter, r *http.Request) (status int, err error) {
	if r.Method != "POST" {
		return http.StatusMethodNotAllowed, nil
	}

	if err := initInvitationTemplate(); err != nil {
		return http.StatusInternalServerError, err
	}

	p, ok := passenger.FromContext(ctx)
	if !ok {
		return http.StatusUnauthorized, nil
	}

	var u model.User
	if err = datastore.Get(ctx, p.User, &u); err != nil {
		return http.StatusInternalServerError, nil
	}

	cKey := u.Company
	if cKey == nil {
		return http.StatusUnauthorized, nil
	}

	var company model.Company
	if err = datastore.Get(ctx, cKey, &company); err != nil {
		return http.StatusInternalServerError, err
	}

	var params = struct {
		Address, Challenge string
	}{}

	if err := json.NewDecoder(r.Body).Decode(&params); err != nil {
		return http.StatusBadRequest, err
	}

	address, err := mail.ParseAddress(params.Address)
	if err != nil {
		return http.StatusBadRequest, err
	}

	challengeKey, err := datastore.DecodeKey(params.Challenge)
	if err != nil {
		return http.StatusBadRequest, err
	}

	var challenge model.Challenge
	if err := datastore.Get(ctx, challengeKey, &challenge); err != nil {
		// TODO(flowlo): Actually look into err. If it is just something like
		// "not found", an internal server error is not appropriate.
		return http.StatusInternalServerError, err
	}

	// TODO(flowlo): Check whether the parent of the current user is the
	// parent of the challenge (if any), and check whether the challenge
	// even exists.

	var users model.Users
	keys, err := model.NewQueryForUser().
		Filter("Address=", address.Address).
		Limit(1).
		GetAll(ctx, &users)

	if err != nil {
		return http.StatusInternalServerError, err
	}

	var key *datastore.Key
	var user model.User
	if len(keys) == 1 {
		key = keys[0]
		user = users[0]
	} else {
		user = model.User{Address: *address}
		key, err = user.Put(ctx, nil)
		if err != nil {
			return http.StatusInternalServerError, err
		}
		profile := model.Profile{}
		if _, err = profile.PutWithParent(ctx, key); err != nil {
			return http.StatusInternalServerError, err
		}
	}

	// NOTE: We are creating a new, orphaned Passenger here, because a
	// Passenger can only issue tokens for the encapsulated user.
	np := passenger.Passenger{
		User: key,
	}

	now := time.Now()
	token := &model.Token{
		Creation:    now,
		Expiry:      now.Add(time.Hour * 24 * 365),
		Description: "Initialization Token",
	}

	value, err := np.IssueToken(ctx, token)
	if err != nil {
		return http.StatusInternalServerError, err
	}

	query := base64.URLEncoding.EncodeToString([]byte(params.Challenge + ":" + value))

	i := model.Invitation{
		User: key,
	}

	// If we're on dev, generate a URL that will point at the dev instance, not
	// production.
	// TODO(flowlo): Move this magic constant somewhere to be configured, as port
	// 6060 is no official thing.
	prefix := "https://app.cod.uno"
	if appengine.IsDevAppServer() {
		prefix = "http://*****:*****@cod.uno>",
		To:      []string{user.Address.String()},
		Subject: "We challenge you!",
		Body:    buf.String(),
	}); err != nil {
		return http.StatusInternalServerError, err
	}

	key, err = i.PutWithParent(ctx, cKey)
	if err != nil {
		return http.StatusInternalServerError, err
	}

	json.NewEncoder(w).Encode(i.Key(key))
	return http.StatusOK, nil
}