Repository: getkin/kin-openapi (Go)

Issue:
Can't delete paths anymore
Hi,
Before https://github.com/getkin/kin-openapi/pull/882 I could delete a path like this:
```
func filterPathsByExtensionInternal(paths *openapi3.Paths, r *regexp.Regexp) {
	for path, pathItem := range paths.Map() {
		for extension := range pathItem.Extensions {
			if r.MatchString(extension) {
				delete(paths.Map(), path)
				break
			}
		}
	}
}
```
After the PR, `Map()` returns a copy rather than the original paths map which makes it impossible to delete paths.
I suggest to add a `Delete` function in [maps.sh](https://github.com/getkin/kin-openapi/blob/master/maps.sh) similar to the `Set` function.
I'm happy to create the PR if this makes sense.


Discussion:
Ah sorry about that. I did mean `Map()` to return a copy (in preparation of the orderedmaps PR).
You're right the solution here is as you describe.

Please send a PR :)
Hello @fenollp 
I opened a PR for this issue
Thanks



Patch:
diff --git a/.github/docs/openapi3.txt b/.github/docs/openapi3.txt
index 44e641f3b..8b146f02c 100644
--- a/.github/docs/openapi3.txt
+++ b/.github/docs/openapi3.txt
@@ -150,6 +150,9 @@ func NewCallback(opts ...NewCallbackOption) *Callback
 func NewCallbackWithCapacity(cap int) *Callback
     NewCallbackWithCapacity builds a callback object of the given capacity.
 
+func (callback *Callback) Delete(key string)
+    Delete removes the entry associated with key 'key' from 'callback'.
+
 func (callback Callback) JSONLookup(token string) (interface{}, error)
     JSONLookup implements
     https://github.com/go-openapi/jsonpointer#JSONPointable
@@ -915,6 +918,9 @@ func NewPaths(opts ...NewPathsOption) *Paths
 func NewPathsWithCapacity(cap int) *Paths
     NewPathsWithCapacity builds a paths object of the given capacity.
 
+func (paths *Paths) Delete(key string)
+    Delete removes the entry associated with key 'key' from 'paths'.
+
 func (paths *Paths) Find(key string) *PathItem
     Find returns a path that matches the key.
 
@@ -1145,6 +1151,9 @@ func NewResponsesWithCapacity(cap int) *Responses
 func (responses *Responses) Default() *ResponseRef
     Default returns the default response
 
+func (responses *Responses) Delete(key string)
+    Delete removes the entry associated with key 'key' from 'responses'.
+
 func (responses Responses) JSONLookup(token string) (interface{}, error)
     JSONLookup implements
     https://github.com/go-openapi/jsonpointer#JSONPointable
diff --git a/maps.sh b/maps.sh
index 4e0789176..0f7e14ca5 100755
--- a/maps.sh
+++ b/maps.sh
@@ -82,7 +82,7 @@ EOF
 }
 
 
-maplike_ValueSetLen() {
+maplike_ValueSetLenDelete() {
 	cat <<EOF >>"$maplike"
 // Value returns the ${name} for key or nil
 func (${name} ${type}) Value(key string) ${value_type} {
@@ -109,6 +109,13 @@ func (${name} ${type}) Len() int {
 	return len(${name}.m)
 }
 
+// Delete removes the entry associated with key 'key' from '${name}'.
+func (${name} ${type}) Delete(key string) {
+	if ${name} != nil && ${name}.m != nil {
+		delete(${name}.m, key)
+	}
+}
+
 // Map returns ${name} as a 'map'.
 // Note: iteration on Go maps is not ordered.
 func (${name} ${type}) Map() (m map[string]${value_type}) {
@@ -213,6 +220,7 @@ test_body() {
 			require.Equal(t, map[string]${value_type}{}, x.Map())
 			require.Equal(t, (${value_type})(nil), x.Value("key"))
 			require.Panics(t, func() { x.Set("key", &${value_type#'*'}{}) })
+			require.NotPanics(t, func() { x.Delete("key") })
 		})
 		t.Run("nonnil", func(t *testing.T) {
 			x := &${type#'*'}{}
@@ -223,6 +231,11 @@ test_body() {
 			require.Equal(t, 1, x.Len())
 			require.Equal(t, map[string]${value_type}{"key": {}}, x.Map())
 			require.Equal(t, &${value_type#'*'}{}, x.Value("key"))
+			x.Delete("key")
+			require.Equal(t, 0, x.Len())
+			require.Equal(t, map[string]${value_type}{}, x.Map())
+			require.Equal(t, (${value_type})(nil), x.Value("key"))
+			require.NotPanics(t, func() { x.Delete("key") })
 		})
 	})
 
@@ -241,7 +254,7 @@ for i in "${!types[@]}"; do
 	name=${names[$i]}
 
 	type="$type" name="$name" value_type="$value_type" maplike_NewWithCapa
-	type="$type" name="$name" value_type="$value_type" maplike_ValueSetLen
+	type="$type" name="$name" value_type="$value_type" maplike_ValueSetLenDelete
 	type="$type" name="$name"    deref_v="$deref_v"    maplike_Pointable
 	type="$type" name="$name" value_type="$value_type" maplike_UnMarsh
 	[[ $((i+1)) != "${#types[@]}" ]] && echo >>"$maplike"
diff --git a/openapi3/maplike.go b/openapi3/maplike.go
index 1f438538e..b27cbf6c5 100644
--- a/openapi3/maplike.go
+++ b/openapi3/maplike.go
@@ -41,6 +41,13 @@ func (responses *Responses) Len() int {
 	return len(responses.m)
 }
 
+// Delete removes the entry associated with key 'key' from 'responses'.
+func (responses *Responses) Delete(key string) {
+	if responses != nil && responses.m != nil {
+		delete(responses.m, key)
+	}
+}
+
 // Map returns responses as a 'map'.
 // Note: iteration on Go maps is not ordered.
 func (responses *Responses) Map() (m map[string]*ResponseRef) {
@@ -153,6 +160,13 @@ func (callback *Callback) Len() int {
 	return len(callback.m)
 }
 
+// Delete removes the entry associated with key 'key' from 'callback'.
+func (callback *Callback) Delete(key string) {
+	if callback != nil && callback.m != nil {
+		delete(callback.m, key)
+	}
+}
+
 // Map returns callback as a 'map'.
 // Note: iteration on Go maps is not ordered.
 func (callback *Callback) Map() (m map[string]*PathItem) {
@@ -265,6 +279,13 @@ func (paths *Paths) Len() int {
 	return len(paths.m)
 }
 
+// Delete removes the entry associated with key 'key' from 'paths'.
+func (paths *Paths) Delete(key string) {
+	if paths != nil && paths.m != nil {
+		delete(paths.m, key)
+	}
+}
+
 // Map returns paths as a 'map'.
 // Note: iteration on Go maps is not ordered.
 func (paths *Paths) Map() (m map[string]*PathItem) {


Test patch:
diff --git a/openapi3/maplike_test.go b/openapi3/maplike_test.go
index 3f92fd68f..fa85ed827 100644
--- a/openapi3/maplike_test.go
+++ b/openapi3/maplike_test.go
@@ -17,6 +17,7 @@ func TestMaplikeMethods(t *testing.T) {
 			require.Equal(t, map[string]*ResponseRef{}, x.Map())
 			require.Equal(t, (*ResponseRef)(nil), x.Value("key"))
 			require.Panics(t, func() { x.Set("key", &ResponseRef{}) })
+			require.NotPanics(t, func() { x.Delete("key") })
 		})
 		t.Run("nonnil", func(t *testing.T) {
 			x := &Responses{}
@@ -27,6 +28,11 @@ func TestMaplikeMethods(t *testing.T) {
 			require.Equal(t, 1, x.Len())
 			require.Equal(t, map[string]*ResponseRef{"key": {}}, x.Map())
 			require.Equal(t, &ResponseRef{}, x.Value("key"))
+			x.Delete("key")
+			require.Equal(t, 0, x.Len())
+			require.Equal(t, map[string]*ResponseRef{}, x.Map())
+			require.Equal(t, (*ResponseRef)(nil), x.Value("key"))
+			require.NotPanics(t, func() { x.Delete("key") })
 		})
 	})
 
@@ -38,6 +44,7 @@ func TestMaplikeMethods(t *testing.T) {
 			require.Equal(t, map[string]*PathItem{}, x.Map())
 			require.Equal(t, (*PathItem)(nil), x.Value("key"))
 			require.Panics(t, func() { x.Set("key", &PathItem{}) })
+			require.NotPanics(t, func() { x.Delete("key") })
 		})
 		t.Run("nonnil", func(t *testing.T) {
 			x := &Callback{}
@@ -48,6 +55,11 @@ func TestMaplikeMethods(t *testing.T) {
 			require.Equal(t, 1, x.Len())
 			require.Equal(t, map[string]*PathItem{"key": {}}, x.Map())
 			require.Equal(t, &PathItem{}, x.Value("key"))
+			x.Delete("key")
+			require.Equal(t, 0, x.Len())
+			require.Equal(t, map[string]*PathItem{}, x.Map())
+			require.Equal(t, (*PathItem)(nil), x.Value("key"))
+			require.NotPanics(t, func() { x.Delete("key") })
 		})
 	})
 
@@ -59,6 +71,7 @@ func TestMaplikeMethods(t *testing.T) {
 			require.Equal(t, map[string]*PathItem{}, x.Map())
 			require.Equal(t, (*PathItem)(nil), x.Value("key"))
 			require.Panics(t, func() { x.Set("key", &PathItem{}) })
+			require.NotPanics(t, func() { x.Delete("key") })
 		})
 		t.Run("nonnil", func(t *testing.T) {
 			x := &Paths{}
@@ -69,6 +82,11 @@ func TestMaplikeMethods(t *testing.T) {
 			require.Equal(t, 1, x.Len())
 			require.Equal(t, map[string]*PathItem{"key": {}}, x.Map())
 			require.Equal(t, &PathItem{}, x.Value("key"))
+			x.Delete("key")
+			require.Equal(t, 0, x.Len())
+			require.Equal(t, map[string]*PathItem{}, x.Map())
+			require.Equal(t, (*PathItem)(nil), x.Value("key"))
+			require.NotPanics(t, func() { x.Delete("key") })
 		})
 	})