Skip to content

Commit

Permalink
[TT-7386] Create api version atomically in OSS (TykTechnologies#4565)
Browse files Browse the repository at this point in the history
  • Loading branch information
furkansenharputlu authored Dec 22, 2022
1 parent 05918db commit ff9ad2f
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 3 deletions.
61 changes: 58 additions & 3 deletions gateway/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1030,8 +1030,12 @@ func (gw *Gateway) handleGetAPIOAS(apiID string, modePublic bool) (interface{},

func (gw *Gateway) handleAddApi(r *http.Request, fs afero.Fs, oasEndpoint bool) (interface{}, int) {
var (
newDef apidef.APIDefinition
oasObj oas.OAS
newDef apidef.APIDefinition
oasObj oas.OAS
baseAPIID = r.FormValue("base_api_id")
baseAPIVersionName = r.FormValue("base_api_version_name")
newVersionName = r.FormValue("new_version_name")
setDefault = r.FormValue("set_default") == "true"
)

if oasEndpoint {
Expand Down Expand Up @@ -1076,6 +1080,58 @@ func (gw *Gateway) handleAddApi(r *http.Request, fs afero.Fs, oasEndpoint bool)
}
}

if baseAPIID != "" {
if baseAPIPtr := gw.getApiSpec(baseAPIID); baseAPIPtr == nil {
log.Errorf("Couldn't find a base API to bind with the given API id: %s", baseAPIID)
} else {
apiInBytes, err := json.Marshal(baseAPIPtr)
if err != nil {
log.WithError(err).Error("Couldn't marshal API spec")
}

var baseAPI APISpec
err = json.Unmarshal(apiInBytes, &baseAPI)
if err != nil {
log.WithError(err).Error("Couldn't unmarshal API spec")
}

baseAPI.VersionDefinition.Enabled = true
if baseAPIVersionName != "" {
baseAPI.VersionDefinition.Name = baseAPIVersionName
baseAPI.VersionDefinition.Default = baseAPIVersionName
}

if baseAPI.VersionDefinition.Key == "" {
baseAPI.VersionDefinition.Key = apidef.DefaultAPIVersionKey
}

if baseAPI.VersionDefinition.Location == "" {
baseAPI.VersionDefinition.Location = apidef.HeaderLocation
}

if baseAPI.VersionDefinition.Default == "" {
baseAPI.VersionDefinition.Default = apidef.Self
}

if baseAPI.VersionDefinition.Versions == nil {
baseAPI.VersionDefinition.Versions = make(map[string]string)
}

baseAPI.VersionDefinition.Versions[newVersionName] = newDef.APIID

if setDefault {
baseAPI.VersionDefinition.Default = newVersionName
}

if !oasEndpoint {
err, _ := gw.writeToFile(fs, baseAPI.APIDefinition, baseAPI.APIID)
if err != nil {
log.WithError(err).Errorf("Error occurred while updating base API with id: %s", baseAPI.APIID)
}
}
}
}

response := apiModifyKeySuccess{
Key: newDef.APIID,
Status: "ok",
Expand Down Expand Up @@ -1810,7 +1866,6 @@ func (gw *Gateway) groupResetHandler(w http.ResponseWriter, r *http.Request) {
// was in the URL parameters, it will block until the reload is done.
// Otherwise, it won't block and fn will be called once the reload is
// finished.
//
func (gw *Gateway) resetHandler(fn func()) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var wg sync.WaitGroup
Expand Down
59 changes: 59 additions & 0 deletions gateway/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2048,6 +2048,65 @@ func TestHandleAddApi(t *testing.T) {

}

func TestHandleAddApi_AddVersionAtomically(t *testing.T) {
ts := StartTest(nil)
defer ts.Close()

const (
baseVersionName = "base-version-name"
v2VersionName = "v2-version-name"
v2APIID = "v2-api-id"
)

baseAPI := ts.Gw.BuildAndLoadAPI(func(a *APISpec) {
a.APIID = "base-api-id"
a.VersionDefinition.Key = ""
a.VersionDefinition.Location = ""
})[0]

v2 := BuildAPI(func(spec *APISpec) {
spec.APIID = v2APIID
})[0]

_, _ = ts.Run(t, []test.TestCase{
{AdminAuth: true, Method: http.MethodPost, Data: v2,
Path: fmt.Sprintf("/tyk/apis?base_api_id=%s&new_version_name=%s&set_default=true&base_api_version_name=%s", baseAPI.APIID, v2VersionName, baseVersionName),
BodyMatchFunc: func(i []byte) bool {
assert.Len(t, baseAPI.VersionDefinition.Versions, 0)
ts.Gw.DoReload()
return true
},
Code: http.StatusOK},
{AdminAuth: true, Method: http.MethodGet, Path: "/tyk/reload", Code: http.StatusOK},
{AdminAuth: true, Path: "/tyk/apis/" + baseAPI.APIID, BodyMatchFunc: func(bytes []byte) bool {
var base apidef.APIDefinition
err := json.Unmarshal(bytes, &base)
assert.NoError(t, err)

expectedVersions := map[string]string{
v2VersionName: v2APIID,
}

assert.Equal(t, expectedVersions, base.VersionDefinition.Versions)
assert.True(t, base.VersionDefinition.Enabled)
assert.Equal(t, apidef.DefaultAPIVersionKey, base.VersionDefinition.Key)
assert.Equal(t, apidef.HeaderLocation, base.VersionDefinition.Location)
assert.Equal(t, baseVersionName, base.VersionDefinition.Name)
assert.Equal(t, v2VersionName, base.VersionDefinition.Default)

return true
}, Code: http.StatusOK},
}...)

_, _ = ts.Run(t, test.TestCase{AdminAuth: true, Path: "/tyk/apis/" + v2APIID, HeadersMatch: baseAPIHeader(baseAPI.APIID), Code: http.StatusOK})
}

func baseAPIHeader(id string) map[string]string {
return map[string]string{
apidef.HeaderBaseAPIID: id,
}
}

func TestHandleUpdateApi(t *testing.T) {
testFs := afero.NewMemMapFs()

Expand Down

0 comments on commit ff9ad2f

Please sign in to comment.