Skip to content
This repository has been archived by the owner on Dec 3, 2021. It is now read-only.

Changes to support Advisor functionality #65

Merged
merged 3 commits into from
Feb 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 26 additions & 8 deletions api/exp/definitions/lessondef.proto
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,21 @@ syntax = "proto3";
package syringe.api.exp;

import "google/api/annotations.proto";
import "google/protobuf/empty.proto";
// import "google/protobuf/empty.proto";
import "validate/validate.proto";

service LessonDefService {

// Retrieve all LessonDefs with filter
rpc ListLessonDefs(google.protobuf.Empty) returns (LessonCategoryMap) {
rpc ListLessonDefs(LessonDefFilter) returns (LessonDefs) {
option (google.api.http) = {
get: "/exp/lessondef/all"
get: "/exp/lessondef"
};
}

rpc GetAllLessonPrereqs(LessonID) returns (LessonPrereqs) {
option (google.api.http) = {
get: "/exp/lessondef/{id}/prereqs"
};
}

Expand All @@ -22,14 +28,19 @@ service LessonDefService {

}

message LessonCategoryMap {
map<string, LessonDefs> lessonCategories = 1;
}
// message LessonCategoryMap {
// map<string, LessonDefs> lessonCategories = 1;
// }


message LessonDefs {
repeated LessonDef lessonDefs = 1;
}

message LessonPrereqs {
repeated int32 prereqs = 1;
}

message LessonID {
int32 id = 1;
}
Expand All @@ -43,14 +54,21 @@ message LessonDef {
repeated Utility Utilities = 6;
repeated Blackbox Blackboxes = 7;
repeated Connection Connections = 8;
string Category = 9 [(validate.rules).string = {in: ["introductory", "troubleshooting", "verification", "configuration"]}];
string Category = 9 [(validate.rules).string = {in: ["fundamentals", "tools", "workflows"]}];
string LessonDiagram = 10;
string LessonVideo = 11;
string Tier = 12 [(validate.rules).string = {in: ["local", "ptr", "prod"]}];
repeated int32 Prereqs = 13;
repeated string Tags = 14;
string Collection = 15;
string Description = 16 [(validate.rules).string.min_len = 10];

// This is meant to fill: "How well do you know <slug>?"
string Slug = 17 [(validate.rules).string.min_len = 1];
}

message LessonDefFilter {
string Category = 1;
string Category = 2;
}

message LessonStage {
Expand Down
83 changes: 70 additions & 13 deletions api/exp/definitions/lessondef.swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,26 @@
"application/json"
],
"paths": {
"/exp/lessondef/all": {
"/exp/lessondef": {
"get": {
"summary": "Retrieve all LessonDefs with filter",
"operationId": "ListLessonDefs",
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/expLessonCategoryMap"
"$ref": "#/definitions/expLessonDefs"
}
}
},
"parameters": [
{
"name": "Category",
"in": "query",
"required": false,
"type": "string"
}
],
"tags": [
"LessonDefService"
]
Expand Down Expand Up @@ -56,6 +64,31 @@
"LessonDefService"
]
}
},
"/exp/lessondef/{id}/prereqs": {
"get": {
"operationId": "GetAllLessonPrereqs",
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/expLessonPrereqs"
}
}
},
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"type": "integer",
"format": "int32"
}
],
"tags": [
"LessonDefService"
]
}
}
},
"definitions": {
Expand Down Expand Up @@ -127,17 +160,6 @@
}
}
},
"expLessonCategoryMap": {
"type": "object",
"properties": {
"lessonCategories": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/expLessonDefs"
}
}
}
},
"expLessonDef": {
"type": "object",
"properties": {
Expand Down Expand Up @@ -195,6 +217,29 @@
},
"Tier": {
"type": "string"
},
"Prereqs": {
"type": "array",
"items": {
"type": "integer",
"format": "int32"
}
},
"Tags": {
"type": "array",
"items": {
"type": "string"
}
},
"Collection": {
"type": "string"
},
"Description": {
"type": "string"
},
"Slug": {
"type": "string",
"title": "This is meant to fill: \"How well do you know \u003cslug\u003e?\""
}
}
},
Expand All @@ -209,6 +254,18 @@
}
}
},
"expLessonPrereqs": {
"type": "object",
"properties": {
"prereqs": {
"type": "array",
"items": {
"type": "integer",
"format": "int32"
}
}
}
},
"expLessonStage": {
"type": "object",
"properties": {
Expand Down
75 changes: 64 additions & 11 deletions api/exp/lessondefs.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,88 @@ import (
"path/filepath"
"reflect"

"github.com/golang/protobuf/ptypes/empty"
pb "github.com/nre-learning/syringe/api/exp/generated"
"github.com/nre-learning/syringe/config"
log "github.com/sirupsen/logrus"
yaml "gopkg.in/yaml.v2"
)

func (s *server) ListLessonDefs(ctx context.Context, _ *empty.Empty) (*pb.LessonCategoryMap, error) {
func (s *server) ListLessonDefs(ctx context.Context, filter *pb.LessonDefFilter) (*pb.LessonDefs, error) {

retMap := map[string]*pb.LessonDefs{}
defs := []*pb.LessonDef{}

// TODO(mierdin): Okay for now, but not super efficient. Should store in category keys when loaded.
for _, lessonDef := range s.scheduler.LessonDefs {

// Initialize category
if _, ok := retMap[lessonDef.Category]; !ok {
retMap[lessonDef.Category] = &pb.LessonDefs{
LessonDefs: []*pb.LessonDef{},
}
for s := range lessonDef.Stages {
lessonDef.Stages[s].LabGuide = ""
}

if filter.Category == "" {
defs = append(defs, lessonDef)
continue
}

retMap[lessonDef.Category].LessonDefs = append(retMap[lessonDef.Category].LessonDefs, lessonDef)
if lessonDef.Category == filter.Category {
defs = append(defs, lessonDef)
}
}

return &pb.LessonCategoryMap{
LessonCategories: retMap,
return &pb.LessonDefs{
LessonDefs: defs,
}, nil
}

// var preReqs []int32

func (s *server) GetAllLessonPrereqs(ctx context.Context, lid *pb.LessonID) (*pb.LessonPrereqs, error) {

// Preload the requested lesson ID so we can strip it before returning
pr := s.getPrereqs(lid.Id, []int32{lid.Id})
log.Debugf("Getting prerequisites for Lesson %d: %d", lid.Id, pr)

return &pb.LessonPrereqs{
// Remove first item from slice - this is the lesson ID being requested
Prereqs: pr[1:],
}, nil
}

func (s *server) getPrereqs(lessonID int32, currentPrereqs []int32) []int32 {

// Return if lesson ID doesn't exist
if _, ok := s.scheduler.LessonDefs[lessonID]; !ok {
return currentPrereqs
}

// Add this lessonID to prereqs if doesn't already exist
if !isAlreadyInSlice(lessonID, currentPrereqs) {
currentPrereqs = append(currentPrereqs, lessonID)
}

// Return if lesson doesn't have prerequisites
lesson := s.scheduler.LessonDefs[lessonID]
if len(lesson.Prereqs) == 0 {
return currentPrereqs
}

// Call recursion for lesson IDs that need it
for i := range lesson.Prereqs {
pid := lesson.Prereqs[i]
currentPrereqs = s.getPrereqs(pid, currentPrereqs)
}

return currentPrereqs
}

func isAlreadyInSlice(lessonID int32, currentPrereqs []int32) bool {
for i := range currentPrereqs {
if currentPrereqs[i] == lessonID {
return true
}
}
return false
}

func (s *server) GetLessonDef(ctx context.Context, lid *pb.LessonID) (*pb.LessonDef, error) {

if _, ok := s.scheduler.LessonDefs[lid.Id]; !ok {
Expand Down
30 changes: 29 additions & 1 deletion api/exp/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"strings"
"sync"

"github.com/golang/glog"
swag "github.com/nre-learning/syringe/api/exp/swagger"

"github.com/nre-learning/syringe/pkg/ui/data/swagger"
Expand Down Expand Up @@ -191,8 +192,35 @@ func grpcHandlerFunc(grpcServer *grpc.Server, otherHandler http.Handler) http.Ha
}
})

// Allow CORS (ONLY IN PREPROD)
// Add gorilla's logging handler for standards-based access logging
return ghandlers.LoggingHandler(os.Stdout, handlerFunc)
return ghandlers.LoggingHandler(os.Stdout, allowCORS(handlerFunc))
}

// allowCORS allows Cross Origin Resoruce Sharing from any origin.
// Don't do this without consideration in production systems.
func allowCORS(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if origin := r.Header.Get("Origin"); origin != "" {
w.Header().Set("Access-Control-Allow-Origin", origin)
if r.Method == "OPTIONS" && r.Header.Get("Access-Control-Request-Method") != "" {
preflightHandler(w, r)
return
}
}
h.ServeHTTP(w, r)
})
}

// preflightHandler adds the necessary headers in order to serve
// CORS from any origin using the methods "GET", "HEAD", "POST", "PUT", "DELETE"
// We insist, don't do this without consideration in production systems.
func preflightHandler(w http.ResponseWriter, r *http.Request) {
headers := []string{"Content-Type", "Accept"}
w.Header().Set("Access-Control-Allow-Headers", strings.Join(headers, ","))
methods := []string{"GET", "HEAD", "POST", "PUT", "DELETE"}
w.Header().Set("Access-Control-Allow-Methods", strings.Join(methods, ","))
glog.Infof("preflight request for %s", r.URL.Path)
}

func serveSwagger(mux *http.ServeMux) {
Expand Down
Loading