How to edit an array that is buried in a recursive struct - go

I have this struct (notice that it is recursive!):
type Group struct {
Name string
Item []string
Groups []Group
}
And I want to append a string to the Item array that is buried deep in the hierarchy of the Group array. The only information I have about the path of this new item is the names of the groups that it's in. Let's say the path is "foo/bar/far". I want to modify bar without overwriting foo, bar or the "root" array. Basically, I want to write a function that returns a new Group variable that is identical to the original variable but with the new string appended.
So far I've tried the following method:
Looping through an array that contains all the group names of the path and if they are in the current group set a current group variable to that new group. Once the loop has finished, append the string to the array and return current group. The only problem is, of course, that the rest of the root group is deleted and replaced with the new, modified group.
The code:
func in(item string, array []Group) (bool, int) {
for i, elem := range array {
if item == elem.Name {
return true, i
} else {
continue
}
}
return false, 0
}
func addItem(list Group, newItem string, path string) Group {
var currentGroup Group = list
if path == "" {
currentGroup.Items = append(currentGroup.Items, newItem)
} else {
for _, elem := range strings.Split(path, "/") {
in, index := in(elem, currentGroup.Groups)
if in {
currentGroup = currentGroup.Groups[index]
}
}
currentGroup.Items = append(currentGroup.Items, newItem)
}
return currentGroup
}

I guess you could pass the group to addItem function as a pointer, and ignore the return value for the function
A bit like
func addItem(list *Group, newItem string, path string) Group {
var currentGroup *Group = list
if path == "" {
currentGroup.Item = append(currentGroup.Item, newItem)
} else {
for _, elem := range strings.Split(path, "/") {
in, index := in(elem, currentGroup.Groups)
if in {
currentGroup = &currentGroup.Groups[index]
}
}
currentGroup.Item = append(currentGroup.Item, newItem)
}
return *currentGroup
}
Complete example at:
https://play.golang.org/p/_1BSF2LDQrE

Related

Create KMS key policy in Go

I'm trying to create a KMS key using the AWS SDK v2 function call:
conn := kms.NewFromConfig(cfg)
input := kms.CreateKeyInput{
KeySpec: types.KeySpecEccNistP521,
KeyUsage: types.KeyUsageTypeSignVerify,
MultiRegion: aws.Bool(true),
Policy: aws.String("")
}
output, err := conn.CreateKey(ctx, &input)
The problem I'm having is that I'm not sure how to generate the policy for the key. I assume I could create JSON for an IAM policy document, but I don't find the prospect of generating that myself to be particularly inviting. Is there a package or library that I can use to generate this document?
I ended up creating my own policy structs:
// Policy describes a policy document that can be used to configure permissions in IAM
type Policy struct {
Version string `json:"Version"`
ID string `json:"Id"`
Statements []*Statement `json:"Statement"`
}
// Statement describes a set of permissions that define what resources and users should have access
// to the resources described therein
type Statement struct {
ID string `json:"Sid"`
Effect Effect `json:"Effect"`
PrincipalArns Principals `json:"Principal"`
ActionArns Actions `json:"Action"`
ResourceArns Resources `json:"Resource"`
}
// Principals describes a list of principals associated with a policy statement
type Principals []string
// MarhsalJSON converts a Principals collection to JSON
func (p Principals) MarshalJSON() ([]byte, error) {
// First, get the inner string from the list of principals
var inner string
if len(p) > 1 {
inner = marshal(p...)
} else if len(p) == 1 {
inner = strings.Quote(p[0], "\"")
} else {
return nil, fmt.Errorf("Principal must contain at least one element")
}
// Next, create the principal block and return it
return []byte(fmt.Sprintf("{\"AWS\": %s}", inner)), nil
}
// Actions describes a list of actions that may or may not be taken by principals with regard to the
// resources described in a policy statement
type Actions []Action
// MarshalJSON converts an Actions collection to JSON
func (a Actions) MarshalJSON() ([]byte, error) {
// First, get the inner string from the list of actions
var inner string
if len(a) > 1 {
inner = marshal(a...)
} else if len(a) == 1 {
inner = strings.Quote(a[0], "\"")
} else {
return nil, fmt.Errorf("Action must contain at least one element")
}
// Next, create the action block and return it
return []byte(inner), nil
}
// Resources describes a list of resources effected by the policy statement
type Resources []string
// MarshalJSON converts a Resources collection to JSON
func (r Resources) MarshalJSON() ([]byte, error) {
// First, get the inner string from the list of actions
var inner string
if len(r) > 1 {
inner = marshal(r...)
} else if len(r) == 1 {
inner = strings.Quote(r[0], "\"")
} else {
return nil, fmt.Errorf("Resource must contain at least one element")
}
// Next, create the action block and return it
return []byte(inner), nil
}
// Helper function that converts a list of items to a JSON-string
func marshal[S ~string](items ...S) string {
return "[" + strings.ModifyAndJoin(func(item string) string { return strings.Quote(item, "\"") }, ",", items...) + "]"
}
// Effect describes the effect a policy statement will have upon the resource and for the actions described
type Effect string
var (
// Allow to grant access of the resource and actions to the principals described in the policy statement
Allow = Effect("Allow")
// Deny to deny access of the resource and actions from the principals described in the policy statement
Deny = Effect("Deny")
)
// Action describes a valid operation that may be made against a particular AWS resource
type Action string
// Describes the various action types available to AWS
var (
CancelKeyDeletion = Action("kms:CancelKeyDeletion")
ConnectCustomKeyStore = Action("kms:ConnectCustomKeyStore")
CreateAlias = Action("kms:CreateAlias")
CreateCustomKeyStore = Action("kms:CreateCustomKeyStore")
CreateGrant = Action("kms:CreateGrant")
CreateKey = Action("kms:CreateKey")
Decrypt = Action("kms:Decrypt")
DeleteAlias = Action("kms:DeleteAlias")
DeleteCustomKeyStore = Action("kms:DeleteCustomKeyStore")
DeleteImportedKeyMaterial = Action("kms:DeleteImportedKeyMaterial")
DescribeCustomKeyStores = Action("kms:DescribeCustomKeyStores")
DescribeKey = Action("kms:DescribeKey")
DisableKey = Action("kms:DisableKey")
DisableKeyRotation = Action("kms:DisableKeyRotation")
DisconnectCustomKeyStore = Action("kms:DisconnectCustomKeyStore")
EnableKey = Action("kms:EnableKey")
EnableKeyRotation = Action("kms:EnableKeyRotation")
Encrypt = Action("kms:Encrypt")
GenerateDataKey = Action("kms:GenerateDataKey")
GenerateDataKeyPair = Action("kms:GenerateDataKeyPair")
GenerateDataKeyPairWithoutPlaintext = Action("kms:GenerateDataKeyPairWithoutPlaintext")
GenerateDataKeyWithoutPlaintext = Action("kms:GenerateDataKeyWithoutPlaintext")
GenerateMac = Action("kms:GenerateMac")
GenerateRandom = Action("kms:GenerateRandom")
GetKeyPolicy = Action("kms:GetKeyPolicy")
GetKeyRotationStatus = Action("kms:GetKeyRotationStatus")
GetParametersForImport = Action("kms:GetParametersForImport")
GetPublicKey = Action("kms:GetPublicKey")
ImportKeyMaterial = Action("kms:ImportKeyMaterial")
ListAliases = Action("kms:ListAliases")
ListGrants = Action("kms:ListGrants")
ListKeyPolicies = Action("kms:ListKeyPolicies")
ListKeys = Action("kms:ListKeys")
ListResourceTags = Action("kms:ListResourceTags")
ListRetirableGrants = Action("kms:ListRetirableGrants")
PutKeyPolicy = Action("kms:PutKeyPolicy")
ReEncryptFrom = Action("kms:ReEncryptFrom")
ReEncryptTo = Action("kms:ReEncryptTo")
ReplicateKey = Action("kms:ReplicateKey")
RetireGrant = Action("kms:RetireGrant")
RevokeGrant = Action("kms:RevokeGrant")
ScheduleKeyDeletion = Action("kms:ScheduleKeyDeletion")
Sign = Action("kms:Sign")
TagResource = Action("kms:TagResource")
UntagResource = Action("kms:UntagResource")
UpdateAlias = Action("kms:UpdateAlias")
UpdateCustomKeyStore = Action("kms:UpdateCustomKeyStore")
UpdateKeyDescription = Action("kms:UpdateKeyDescription")
UpdatePrimaryRegion = Action("kms:UpdatePrimaryRegion")
Verify = Action("kms:Verify")
VerifyMac = Action("kms:VerifyMac")
KmsAll = Action("kms:*")
)
I can then use this in my code like so:
conn := kms.NewFromConfig(cfg)
policy := Policy {
Version: "2012-10-17",
ID: "test-key",
Statements: []*policy.Statement{
{
ID: "test-failure",
Effect: policy.Allow,
PrincipalArns: []string{"arn:aws:kms:eu-west-2:111122223333:root"},
ActionArns: policy.Actions{policy.KmsAll},
ResourceArns: []string{"*"},
},
},
}
pData, err := json.Marshal(policy)
if err != nil {
return err
}
input := kms.CreateKeyInput{
KeySpec: types.KeySpecEccNistP521,
KeyUsage: types.KeyUsageTypeSignVerify,
MultiRegion: aws.Bool(true),
Policy: aws.String(string(pData)),
}
output, err := conn.CreateKey(ctx, &input)
I added the code for this in an open-source package that can be found here so others can use it.

Convert string in struct to []string

I have a struct as below:
type TourData struct {
ArtistID int //artist ID
RelationID string //key for relations
City string
Country string
TourDates []string
}
type MyRelation struct {
ID int `json:"id"`
DatesLocations map[string][]string `json:"datesLocations"`
}
which contains this data from a csv file:
1,nagoya-japan,Nagoya,Japan,
1,penrose-new_zealand,Penrose,New_Zealand,
1,dunedin-new_zealand,Dunedin,New_Zealand,
2,playa_del_carmen-mexico,Playa Del Carmen,Mexico,
2,papeete-french_polynesia,Papeete,French_Polynesia,
MyRelations is populated from an API which contains:
"index": [
{
"id": 1,
"datesLocations": {
"dunedin-new_zealand": [
"10-02-2020"
],
"nagoya-japan": [
"30-01-2019"
],
"penrose-new_zealand": [
"07-02-2020"
]
}
},
{
"id": 2,
"datesLocations": {
"papeete-french_polynesia": [
"16-11-2019"
],
"playa_del_carmen-mexico": [
"05-12-2019",
"06-12-2019",
"07-12-2019",
"08-12-2019",
"09-12-2019"
]
}
}
The dates come from another struct. The code I have used to populate this struct is as below:
var oneRecord TourData
var allRecords []TourData
for _, each := range csvData {
oneRecord.ArtistID, _ = strconv.Atoi(each[0])
oneRecord.RelationID = each[1]
oneRecord.City = each[2]
oneRecord.Country = each[3]
oneRecord.TourDates = Relations.Index[oneRecord.ArtistID-1].DatesLocations[each[1]]
allRecords = append(allRecords, oneRecord)
}
jsondata, err := json.Marshal(allRecords) // convert to JSON
json.Unmarshal(jsondata, &TourThings)
I need to group all the 1s together then the 2s etc. I thought to create another struct, and populate from this one but not having much luck - any ideas?
To clarify I would want say TourData.City to equal:
[Nagoya,Penrose,Dunedin]
[Playa Del Carmen, Papeete]
At the moment if I was to print TourData[0].City I would get Nagoya.
I have tried creating another struct to be populated from the TourData struct with the following fields:
type TourDataArrays struct {
ArtistID int
City []string
Country []string
TourDates [][]string
}
and then populate the struct using the code below:
var tourRecord TourDataArrays
var tourRecords []TourDataArrays
for i := 0; i < len(Relations.Index); i++ {
for j := 0; j < len(allRecords); j++ {
if allRecords[i].ArtistID == i+1 {
tourRecord.City = append(tourRecord.City, allRecords[j].City)
}
}
tourRecords = append(tourRecords, tourRecord)
}
However this is adding all the cities to one array i.e
[Nagoya, Penrose, Dunedin, Playa Del Carmen, Papeete].
If I understand your requirements correctly you needed to declare city as a string array as well. (And Country to go with it).
Check out this solution : https://go.dev/play/p/osgkbfWV3c5
Note I have not deduped country and derived city and country from one field in the Json.

How work history of web browser under the hood

I am trying to implement my own web browser history for WKWebView on iOS, but I can't implement this functionality completely, and each time I obtain trouble.
I can create a history where the user did be and then moving forward and backward inside history.
But I have next trouble, and I think it an only one of many problems on my way.
When I have a history with for example 10 elements, and then I am moving back to element number 5 and then go don't forward but try to open the new link I can't remove element 6-10 and put the new link.
I think my problem that I can't fully understand how history work inside all browsers under the hood, this is not a hard task but I am confused inside this algorithm.
My main data structure for holding history
Help me understand how to work this algorithm inside browsers or maybe exist a good theory about it?
I have solved this problem and realize the full algorithm well, the completed project available here: https://github.com/IhorYachmenov/Custom-browser-history-for-WKWebView.
Algorithm:
struct PlayerHistory {
static var shared = PlayerHistory()
var historyExist: Bool = false
var historyCurrentPosition: Int = 0
var historyLastPositionBeforeUpdatingHistory: Int!
var userHistoryKey: String!
var backPressed: Bool!
var forwardPressed: Bool!
var urlOfPlayer: String!
// Function only for first loading inside <viewDidLoad or another method from app LifeCycle>.
mutating func getUrlForFirstLoading(initURL: String, key: String) -> String {
urlOfPlayer = initURL
guard HistoryStorage.shared.getHistoryFromUserDefaults() != nil else {
updateFirstElement(key: key, url: initURL)
return initURL
}
guard HistoryStorage.shared.getHistoryFromUserDefaults()![key] != nil else {
return initURL
}
let position = HistoryStorage.shared.getHistoryFromUserDefaults()![key]!.count - 1
historyExist = true
historyCurrentPosition = position
userHistoryKey = key
let initUrlFromHistoryStorage = HistoryStorage.shared.getHistoryFromUserDefaults()![key]!.last!.url
return initUrlFromHistoryStorage
}
// Create new or update exist history, use this method indsede <decidePolicyForNavigation>.
mutating func updatePlayerHistory(backlisk: [String], key: String) {
var history = [WebViewHistory]()
for i in backlisk {
history.append(WebViewHistory(i))
}
if (historyExist == true) {
// If old history exist need compound both and then to save.
let oldHistory = HistoryStorage.shared.getHistoryFromUserDefaults()![key]
let oldAndNewHostoryTogether = oldHistory! + history
var keyValuePair = Dictionary<String, [WebViewHistory]>()
keyValuePair.updateValue(oldAndNewHostoryTogether, forKey: key)
HistoryStorage.shared.removeHistory()
HistoryStorage.shared.saveHistory(keyValuePair)
setCurrentPosition(url: backlisk.last!, key: key)
} else {
var keyValuePair = Dictionary<String, [WebViewHistory]>()
keyValuePair.updateValue(history, forKey: key)
historyExist = true
HistoryStorage.shared.removeHistory()
HistoryStorage.shared.saveHistory(keyValuePair)
setCurrentPosition(url: backlisk.last!, key: key)
}
}
// Before using this method check if result don't equals nil. Use this method for navigation beetween history
func moveThroughHistory(key: String, direction: Bool) -> String? {
guard historyExist != false else {
return nil
}
let history = HistoryStorage.shared.getHistoryFromUserDefaults()![key]!
if (direction == true) {
let index = historyCurrentPosition + 1
guard index != history.count else { return nil }
return history[index].url
} else {
let index = historyCurrentPosition - 1
guard index > 0 else { return history[0].url }
return history[index].url
}
}
// Method <setCurrentPosition> each time set position at history
mutating func setCurrentPosition(url: String, key: String) {
guard HistoryStorage.shared.getHistoryFromUserDefaults() != nil else { return }
guard HistoryStorage.shared.getHistoryFromUserDefaults()![key] != nil else { return }
let history = HistoryStorage.shared.getHistoryFromUserDefaults()![key]
let index = history?.firstIndex(of: WebViewHistory(url))
guard index != nil else {
historyCurrentPosition = 0
return
}
historyCurrentPosition = index!
}
// <removeUnusedPeaceOfHistory> need use when user want open new page staying inside the middle of history
mutating func removeUnusedPeaceOfHistory(key: String) {
guard HistoryStorage.shared.getHistoryFromUserDefaults() != nil else {
return
}
guard HistoryStorage.shared.getHistoryFromUserDefaults()![key] != nil else {
return
}
var history = HistoryStorage.shared.getHistoryFromUserDefaults()![key]!
let startIndex = historyCurrentPosition + 1
let endIndex = history.endIndex - 1
let countOfAllElements = history.count
guard startIndex != countOfAllElements else { return }
let range = startIndex...endIndex
history.removeSubrange(range)
var keyValuePair = Dictionary<String, [WebViewHistory]>()
keyValuePair.updateValue(history, forKey: key)
HistoryStorage.shared.removeHistory()
HistoryStorage.shared.saveHistory(keyValuePair)
}
// Use <updateFirstElement> inside <getUrlForFirstLoading> if history doesn't exist
private mutating func updateFirstElement(key: String, url: String) {
var history = [WebViewHistory]()
history.insert(WebViewHistory(url), at: 0)
var keyValuePair = Dictionary<String, [WebViewHistory]>()
keyValuePair.updateValue(history, forKey: key)
HistoryStorage.shared.saveHistory(keyValuePair)
historyExist = true
historyCurrentPosition = 0
}
// Use <webViewWillBeClosedSaveHistory> when WKWebView should be closed, if the user moves through history new position will be saved.
mutating func webViewWillBeClosedSaveHistory(key: String) {
let history = HistoryStorage.shared.getHistoryFromUserDefaults()![key]!
let currentPosition = historyCurrentPosition + 1
guard currentPosition != history.count else { return }
removeUnusedPeaceOfHistory(key: key)
}
}

Assign value to field in struct if empty

I have a struct defined
type data struct {
invitecode string
fname string
lname string
}
which I populate from retrieving form data after parsing
...
r.ParseForm()
new user := &data{
invitecode: r.FormValue("invitecode"),
fname: r.FormValue("fname")
lname: r.FormValue("lname")
}
I did like to check if the invitecode field obtained from the form is empty and if so, populate it by calling a function but if it is not, to populate it with the retrieved value (invitecode: if newUser.invitecode == "" {"Mr"} else {lnames.title},). I understand go doesn't have a tenary operator which I thought of using and reading the questions here, here, here & here implies using an if else statement but I can't seem to get it to work. Preferable, I am looking for a solution that check's while assigning a new variable. Trying the code below doesn't seem to work. Any help would be appreciated.
package main
import (
"fmt"
)
type data struct {
invitecode string
fname string
lname string
}
func main() {
var user data
newUser := map[string]string{"invitecode": "", "fname": "Dude", "lname": "Did"}
user = &data{
invitecode: if newUser.invitecode == "" {"Mr"} else {lnames.title},
fname: newUser.fname,
lname: newUser.lname,
}
fmt.Println(user)
}
You cannot use an if ... else statement inline like you would a ternary operator (or if/else statements) in other languages, you must simply do it procedurally:
user := &data{ /* ... */ }
if user.invitecode == "" {
user.invitecode = "Mr"
} else {
user.invitecode = lnames.title
}
Go does not have ternaries, nor can you do an inline if like you've shown in the code. You will have to do a normal if block:
user = &data{}
if newUser.inviteCode == "" {
user.invitecode = "Mr"
} else {
user.invitecode = lnames.title
}
And so on. You could extract this into a function:
func coalesce(args ...string) string {
for _,str := range args {
if str != "" {
return str
}
}
return ""
}
And use it like so:
user.invitecode = coalesce(lnames.title, "Mr")
Of course, if you deal with multiple types (not just strings), you'll need one such function for each type.

Filtering multiple times on one dictionary

I currently run this code:
searchterm = "test"
results = resultsArray.filter { $0.description.contains (searchterm!) }
My question is how do I search in company_name or place or any other field in my model and add it to the results.
Do I need to use filters together and then append the results to a new variable instance of my model?
EDIT:
If "test" is in company_name, place and description. I want all three results returned. However, if "test" is only in place, I need only place to be returned.
EDIT2:
This is an example of my model return. Is this a dictionary or an array? I'm sorry I dont 100% percent know the difference. I know ' "this": is ' what a dictionary looks like, however because there were [] brackets around them, I thought that made it an array...
struct GraphData {
var description: String
var company_name: String
var places: String
init(description: String, company_name: String, places: String){
self.description = description
self.company_name = company_name
self.places = places
}
func toAnyObject() -> Any {
print("return")
return [
"description": description,
"company_name": company_name,
"places": places,
]
}
The easiest way to do this would be to create a custom contains method in your model which can you can use to match the search term against any property in the model:
class YourModel {
var company_name: String
var description: String
var place: String
// ...
func contains(_ searchTerm: String) -> Bool {
return self.company_name.contains(searchTerm)
|| self.description.contains(searchTerm)
|| self.place.contains(searchTerm)
}
}
You can then simply filter using your custom method:
let searchTerm = "test"
let results = resultsArray.filter { $0.contains(searchTerm) }
Is this resultsArray a dictionary or an array?
You can do something like this
let searchTerm = "test"
let filter = resultsArray.filter{ $0.company_name!.contains(searchTerm) || $0.place!.contains(searchTerm) }
Edit
class TestClass: NSObject {
var place: String?
var company_name: String?
func contain(searchTerm: String) -> [String] {
var result = [String]()
if let placeVal = place, placeVal.contains(searchTerm) {
result.append(placeVal)
}
if let companyVal = company_name, companyVal.contains(searchTerm) {
result.append(companyVal)
}
return result
}
}
let searchTerm = "test"
let filter = resultsArray.map { $0.contain(searchTerm: searchTerm) }

Resources