Skip to content
Snippets Groups Projects
main.go 16.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	"code.vereign.com/code/restful-api/server"
    
    	"reflect"
    	"sort"
    
    	"unicode"
    
    	newpath := filepath.Join(".", "javascript/temp")
    	os.MkdirAll(newpath, os.ModePerm)
    
    
    	viamapi := buildViamAPI()
    
    	err := ioutil.WriteFile("./javascript/temp/viamapi.js", []byte(viamapi), 0644)
    
    
    	if err != nil {
    		fmt.Println(err.Error())
    	}
    
    	penpalMethods := buildPenpalMethods()
    	err = ioutil.WriteFile("./javascript/temp/penpal-methods.js", []byte(penpalMethods), 0644)
    
    
    	if err != nil {
    		fmt.Println(err.Error())
    	}
    
    }
    
    func buildPenpalMethods() string {
    	prefixes := []string{}
    	endPoints := server.GetEndPoints(prefixes)
    
    	result :=
    		"import Penpal from 'penpal';\n\n" +
    			"export default {\n"
    
    Markin Igor's avatar
    Markin Igor committed
    	methods := generatePenpalRemoteMethods(endPoints)
    
    	methods += getWopiAPIPenpalMethods()
    
    
    	result += methods
    
    	result += "\n}"
    
    	return result
    }
    
    func buildViamAPI() string {
    
    	prefixes := []string{}
    	endPoints := server.GetEndPoints(prefixes)
    
    Markin Igor's avatar
    Markin Igor committed
    
    
    Markin Igor's avatar
    Markin Igor committed
    	result := "const axios = require('axios');\n"
    
    
    	var keys []string
    	for k := range endPoints {
    		keys = append(keys, k)
    
    Markin Igor's avatar
    Markin Igor committed
    	}
    
    
    	sort.Strings(keys)
    
    	keysLen := len(keys)
    
    	result += "function ViamAPI() {\n" +
    
    		"    this.config = {\n" +
    		"        headers: {\n" +
    		"            'publicKey': '',\n" +
    		"            'uuid': '',\n" +
    
    		"            'deviceHash': '',\n" +
    		"            'userAgent': '',\n" +
    
    		"            'token': ''\n" +
    		"        }\n" +
    		"    }\n" +
    		"}\n\n"
    
    
    
    	result += "ViamAPI.prototype.setSessionData = function(uuid, token, deviceHash, userAgent) {\n" +
    		"    this.config.headers.uuid = uuid;\n" +
    		"    this.config.headers.token = token;\n" +
    		"    this.config.headers.deviceHash = deviceHash;\n" +
    		"    this.config.headers.userAgent = userAgent;\n" +
    
    
    	result += "ViamAPI.prototype.setIdentity = function(authenticationPublicKey) {\n" +
    
    		"    this.config.headers.publicKey = window.btoa(authenticationPublicKey);\n" +
    
    
    	result += "ViamAPI.prototype.getConfig = function() {\n" +
    		"    return this.config;\n" +
    
    
    	for i := 0; i < keysLen; i++ {
    		url := keys[i]
    		if endPoints[url].Form != nil {
    			splits := strings.Split(url, "/")
    
    			packageStr := splits[len(splits)-2]
    
    			/*if !packageCreated[packageStr] {
    				result += "ViamAPI.prototype.\"" + packageStr + "\" = {}\n\n"
    				packageCreated[packageStr] = true
    			}*/
    
    			methodStr := splits[len(splits)-1]
    
    			form := endPoints[url].Form
    			s := reflect.ValueOf(form)
    
    			typeOfT := s.Type()
    
    			fields := typeFields(typeOfT)
    
    			lenFields := len(fields)
    
    			args := ""
    
    			for i := 0; i < lenFields; i++ {
    				if i != lenFields-1 {
    					args += fmt.Sprintf("%sArg,", fields[i].name)
    				} else {
    					args += fmt.Sprintf("%sArg", fields[i].name)
    				}
    			}
    
    			result += "ViamAPI.prototype." + packageStr + strings.Title(methodStr) + " = function(" + args + ") {\n" +
    
    Markin Igor's avatar
    Markin Igor committed
    				"    return axios.post(window.API_HOST + '" + packageStr + "/" + methodStr + "', {\n"
    
    
    			for i := 0; i < lenFields; i++ {
    				result += "        " + fields[i].name + ": " + fields[i].name + "Arg"
    				if i != lenFields-1 {
    					result += ","
    				}
    				result += "\n"
    			}
    
    			result += "    }, this.config);\n" +
    
    		} else {
    			splits := strings.Split(url, "/")
    
    			packageStr := splits[len(splits)-2]
    
    			/*if !packageCreated[packageStr] {
    				result += "api[\"" + packageStr + "\"] = {}\n"
    				packageCreated[packageStr] = true
    			}*/
    
    			methodStr := splits[len(splits)-1]
    
    			result += "ViamAPI.prototype." + packageStr + strings.Title(methodStr) + " = function() {\n" +
    
    Markin Igor's avatar
    Markin Igor committed
    				"    return axios.post(window.API_HOST + '" + packageStr + "/" + methodStr + "', {}, this.config);\n" +
    
    	result += "module.exports = ViamAPI;\n"
    
    
    	return result
    }
    
    
    // A field represents a single field found in a struct.
    type field struct {
    	name      string
    	nameBytes []byte                 // []byte(name)
    
    	tag       bool
    	index     []int
    	typ       reflect.Type
    	omitEmpty bool
    	quoted    bool
    }
    
    func fillField(f field) field {
    	f.nameBytes = []byte(f.name)
    	return f
    }
    
    // byIndex sorts field by index sequence.
    type byIndex []field
    
    func (x byIndex) Len() int { return len(x) }
    
    func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
    
    func (x byIndex) Less(i, j int) bool {
    	for k, xik := range x[i].index {
    		if k >= len(x[j].index) {
    			return false
    		}
    		if xik != x[j].index[k] {
    			return xik < x[j].index[k]
    		}
    	}
    	return len(x[i].index) < len(x[j].index)
    }
    
    func isValidTag(s string) bool {
    	if s == "" {
    		return false
    	}
    	for _, c := range s {
    		switch {
    		case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", c):
    			// Backslash and quote chars are reserved, but
    			// otherwise any punctuation chars are allowed
    			// in a tag name.
    		default:
    			if !unicode.IsLetter(c) && !unicode.IsDigit(c) {
    				return false
    			}
    		}
    	}
    	return true
    }
    
    // tagOptions is the string following a comma in a struct field's "json"
    // tag, or the empty string. It does not include the leading comma.
    type tagOptions string
    
    // Contains reports whether a comma-separated list of options
    // contains a particular substr flag. substr must be surrounded by a
    // string boundary or commas.
    func (o tagOptions) Contains(optionName string) bool {
    	if len(o) == 0 {
    		return false
    	}
    	s := string(o)
    	for s != "" {
    		var next string
    		i := strings.Index(s, ",")
    		if i >= 0 {
    			s, next = s[:i], s[i+1:]
    		}
    		if s == optionName {
    			return true
    		}
    		s = next
    	}
    	return false
    }
    
    // parseTag splits a struct field's json tag into its name and
    // comma-separated options.
    func parseTag(tag string) (string, tagOptions) {
    	if idx := strings.Index(tag, ","); idx != -1 {
    		return tag[:idx], tagOptions(tag[idx+1:])
    	}
    	return tag, tagOptions("")
    }
    
    // typeFields returns a list of fields that JSON should recognize for the given type.
    // The algorithm is breadth-first search over the set of structs to include - the top struct
    // and then any reachable anonymous structs.
    func typeFields(t reflect.Type) []field {
    	// Anonymous fields to explore at the current level and the next.
    	current := []field{}
    	next := []field{{typ: t}}
    
    	// Count of queued names for current level and the next.
    	count := map[reflect.Type]int{}
    	nextCount := map[reflect.Type]int{}
    
    	// Types already visited at an earlier level.
    	visited := map[reflect.Type]bool{}
    
    	// Fields found.
    	var fields []field
    
    	for len(next) > 0 {
    		current, next = next, current[:0]
    		count, nextCount = nextCount, map[reflect.Type]int{}
    
    		for _, f := range current {
    			if visited[f.typ] {
    				continue
    			}
    			visited[f.typ] = true
    
    			// Scan f.typ for fields to include.
    			for i := 0; i < f.typ.NumField(); i++ {
    				sf := f.typ.Field(i)
    				isUnexported := sf.PkgPath != ""
    				if sf.Anonymous {
    					t := sf.Type
    					if t.Kind() == reflect.Ptr {
    						t = t.Elem()
    					}
    					if isUnexported && t.Kind() != reflect.Struct {
    						// Ignore embedded fields of unexported non-struct types.
    						continue
    					}
    					// Do not ignore embedded fields of unexported struct types
    					// since they may have exported fields.
    				} else if isUnexported {
    					// Ignore unexported non-embedded fields.
    					continue
    				}
    				tag := sf.Tag.Get("json")
    				if tag == "-" {
    					continue
    				}
    				name, opts := parseTag(tag)
    				if !isValidTag(name) {
    					name = ""
    				}
    				index := make([]int, len(f.index)+1)
    				copy(index, f.index)
    				index[len(f.index)] = i
    
    				ft := sf.Type
    				if ft.Name() == "" && ft.Kind() == reflect.Ptr {
    					// Follow pointer.
    					ft = ft.Elem()
    				}
    
    				// Only strings, floats, integers, and booleans can be quoted.
    				quoted := false
    				if opts.Contains("string") {
    					switch ft.Kind() {
    					case reflect.Bool,
    						reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
    						reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
    						reflect.Float32, reflect.Float64,
    						reflect.String:
    						quoted = true
    					}
    				}
    
    				// Record found field and index sequence.
    				if name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct {
    					tagged := name != ""
    					if name == "" {
    						name = sf.Name
    					}
    					fields = append(fields, fillField(field{
    						name:      name,
    						tag:       tagged,
    						index:     index,
    						typ:       ft,
    						omitEmpty: opts.Contains("omitempty"),
    						quoted:    quoted,
    					}))
    					if count[f.typ] > 1 {
    						// If there were multiple instances, add a second,
    						// so that the annihilation code will see a duplicate.
    						// It only cares about the distinction between 1 or 2,
    						// so don't bother generating any more copies.
    						fields = append(fields, fields[len(fields)-1])
    					}
    					continue
    				}
    
    				// Record new anonymous struct to explore in next round.
    				nextCount[ft]++
    				if nextCount[ft] == 1 {
    					next = append(next, fillField(field{name: ft.Name(), index: index, typ: ft}))
    				}
    			}
    		}
    	}
    
    	sort.Slice(fields, func(i, j int) bool {
    		x := fields
    		// sort field by name, breaking ties with depth, then
    		// breaking ties with "name came from json tag", then
    		// breaking ties with index sequence.
    		if x[i].name != x[j].name {
    			return x[i].name < x[j].name
    		}
    		if len(x[i].index) != len(x[j].index) {
    			return len(x[i].index) < len(x[j].index)
    		}
    		if x[i].tag != x[j].tag {
    			return x[i].tag
    		}
    		return byIndex(x).Less(i, j)
    	})
    
    	// Delete all fields that are hidden by the Go rules for embedded fields,
    	// except that fields with JSON tags are promoted.
    
    	// The fields are sorted in primary order of name, secondary order
    	// of field index length. Loop over names; for each name, delete
    	// hidden fields by choosing the one dominant field that survives.
    	out := fields[:0]
    	for advance, i := 0, 0; i < len(fields); i += advance {
    		// One iteration per name.
    		// Find the sequence of fields with the name of this first field.
    		fi := fields[i]
    		name := fi.name
    		for advance = 1; i+advance < len(fields); advance++ {
    			fj := fields[i+advance]
    			if fj.name != name {
    				break
    			}
    		}
    		if advance == 1 { // Only one field with this name
    			out = append(out, fi)
    			continue
    		}
    		dominant, ok := dominantField(fields[i : i+advance])
    		if ok {
    			out = append(out, dominant)
    		}
    	}
    
    	fields = out
    	sort.Sort(byIndex(fields))
    
    	return fields
    }
    
    // dominantField looks through the fields, all of which are known to
    // have the same name, to find the single field that dominates the
    // others using Go's embedding rules, modified by the presence of
    // JSON tags. If there are multiple top-level fields, the boolean
    // will be false: This condition is an error in Go and we skip all
    // the fields.
    func dominantField(fields []field) (field, bool) {
    	// The fields are sorted in increasing index-length order. The winner
    	// must therefore be one with the shortest index length. Drop all
    	// longer entries, which is easy: just truncate the slice.
    	length := len(fields[0].index)
    	tagged := -1 // Index of first tagged field.
    	for i, f := range fields {
    		if len(f.index) > length {
    			fields = fields[:i]
    			break
    		}
    		if f.tag {
    			if tagged >= 0 {
    				// Multiple tagged fields at the same level: conflict.
    				// Return no field.
    				return field{}, false
    			}
    			tagged = i
    		}
    	}
    	if tagged >= 0 {
    		return fields[tagged], true
    	}
    	// All remaining fields have the same length. If there's more than one,
    	// we have a conflict (two fields named "X" at the same level) and we
    	// return no field.
    	if len(fields) > 1 {
    		return field{}, false
    	}
    	return fields[0], true
    }
    
    
    func generatePenpalRemoteMethods(endPoints map[string]*server.EndPoint) string {
    
    	var keys []string
    	for k := range endPoints {
    		keys = append(keys, k)
    	}
    
    	sort.Strings(keys)
    
    	keysLen := len(keys)
    
    	methods := ""
    
    	privateCheckSnippet := `
    
            const authenticationPublicKey = localStorage.getItem("authenticatedIdentity");
    
    	
    	    if (authenticationPublicKey === null) {
    		    result({
    			    "data" : "",
    			    "code" : "400",
    			    "status" : "Identity not authenticated"
    		    });
    	    }
    	
    	    if (loadedIdentities[authenticationPublicKey] === null) {
    		    result({
    			    "data" : "",
    			    "code" : "400",
    			    "status" : "Identity not authenticated"
    		    });
     	    } 
    	
    
    	    const success = extendPinCodeTtl(authenticationPublicKey);
    
    	    if(success === false) {
    
    		    result({"data" : "",
    			    "code" : "400",
    			    "status" : "Identity not authenticated"
    		    })
    		}
    		
    	`
    
    	for i := 0; i < keysLen; i++ {
    		url := keys[i]
    
    		if endPoints[url].ManuallyWritten == true {
    			continue
    		}
    
    		if url == "/identity/getIdentityProfileData" {
    			privateCheckSnippet = `
    
    				const authenticationPublicKey = localStorage.getItem("authenticatedIdentity");
    
    			
    				if (authenticationPublicKey === null) {
    					result({
    						"data" : "",
    						"code" : "400",
    						"status" : "Identity not authenticated"
    					});
    				}
    			
    				if (loadedIdentities[authenticationPublicKey] === null) {
    					result({
    						"data" : "",
    						"code" : "400",
    						"status" : "Identity not authenticated"
    					});
    				} 
    							
    			`
    		}
    
    		if endPoints[url].Form != nil {
    			splits := strings.Split(url, "/")
    
    			packageStr := splits[len(splits)-2]
    
    			/*if !packageCreated[packageStr] {
    				result += "ViamAPI.prototype.\"" + packageStr + "\" = {}\n\n"
    				packageCreated[packageStr] = true
    			}*/
    
    			methodStr := splits[len(splits)-1]
    
    			form := endPoints[url].Form
    			s := reflect.ValueOf(form)
    
    			typeOfT := s.Type()
    
    			fields := typeFields(typeOfT)
    
    			lenFields := len(fields)
    
    			args := ""
    
    			for i := 0; i < lenFields; i++ {
    				if i != lenFields-1 {
    					args += fmt.Sprintf("%sArg,", fields[i].name)
    				} else {
    					args += fmt.Sprintf("%sArg", fields[i].name)
    				}
    			}
    
    			/*identity_login(modeArg,codeArg,actionIDArg) {
    					return new Penpal.Promise(result => {
    						viamApi.identity_login(modeArg,codeArg,actionIDArg).then((response) => { result(response.data);});
    					});
    			}*/
    
    			snippet := ""
    
    			if endPoints[url].HandlerType == "private" {
    				snippet = privateCheckSnippet
    			}
    
    			lastComma := ","
    
    			if args == "" {
    				lastComma = ""
    			}
    
    			method := packageStr + strings.Title(methodStr) + ": function(" + args + ") {\n" +
    				"    return new Penpal.Promise(function(result) {\n" + snippet +
    				"    executeRestfulFunction(\"" + endPoints[url].HandlerType + "\", viamApi, viamApi." + packageStr + strings.Title(methodStr) + lastComma + args + ").then(function(executeResult) {\n" +
    				"            result(executeResult);\n" +
    				"        });\n" +
    				"    });\n" +
    				"}"
    
    			methods += method
    
    			if i != keysLen-1 {
    				methods += ","
    			}
    
    			methods += "\n"
    		} else {
    			splits := strings.Split(url, "/")
    
    			packageStr := splits[len(splits)-2]
    
    			/*if !packageCreated[packageStr] {
    				result += "ViamAPI.prototype.\"" + packageStr + "\" = {}\n\n"
    				packageCreated[packageStr] = true
    			}*/
    
    			methodStr := splits[len(splits)-1]
    
    			snippet := ""
    
    			if endPoints[url].HandlerType == "private" {
    				snippet = privateCheckSnippet
    			}
    
    			method := packageStr + strings.Title(methodStr) + ": function() {\n" +
    				"    return new Penpal.Promise(function(result) {\n" + snippet +
    				"    executeRestfulFunction(\"" + endPoints[url].HandlerType + "\", viamApi, viamApi." + packageStr + strings.Title(methodStr) + ").then(function(executeResult) {\n" +
    
    				"            result(executeResult);\n" +
    
    				"        });\n" +
    				"    });\n" +
    				"}"
    
    			methods += method
    
    			if i != keysLen-1 {
    				methods += ","
    			}
    
    			methods += "\n"
    
    		}
    	}
    
    	return methods
    }
    
    func getWopiAPIPenpalMethods() string {
    	return `getPassports: function(fileID) {
    		return new Penpal.Promise(function(result) {
    
    			const authenticationPublicKey = localStorage.getItem("authenticatedIdentity");
    
    			if (authenticationPublicKey === null) {
    				result({
    					"data" : "",
    					"code" : "400",
    					"status" : "Identity not authenticated"
    				});
    			}
    			
    			if (loadedIdentities[authenticationPublicKey] === null) {
    				result({
    					"data" : "",
    					"code" : "400",
    					"status" : "Identity not authenticated"
    				});
    			}
    			
    
    			const success = extendPinCodeTtl(authenticationPublicKey);
    
    			if (success === false) {
    
    				result({"data" : "",
    					"code" : "400",
    					"status" : "Identity not authenticated"
    
    			wopiAPI.getPassports(fileID).then(function(response) {
    
    				result(response.data);
    			});
    		});
    	}`
    }