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

Commit

Permalink
Merge pull request #65 from nre-learning/advisor
Browse files Browse the repository at this point in the history
Changes to support Advisor functionality
  • Loading branch information
Mierdin authored Feb 5, 2019
2 parents c1f1c8d + 9f4e7b0 commit c1464b1
Show file tree
Hide file tree
Showing 5 changed files with 259 additions and 46 deletions.
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

0 comments on commit c1464b1

Please sign in to comment.