diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..c25285c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,40 @@ +name: Bug report +description: Create a report to help us improve +title: '[bug]' +labels: ['bug'] +body: + - type: textarea + attributes: + label: Describe the bug + description: A clear and concise description of what the bug is + validations: + required: false + - type: textarea + attributes: + label: To Reproduce + description: | + Steps to reproduce the behavior. + 1. Go to '...' + 2. Click on '...' + 3. Scroll down to '...' + 4. See error + validations: + required: false + - type: textarea + attributes: + label: Expected Behavior + description: A clear and concise description of what you expected to happen. + validations: + required: false + - type: textarea + attributes: + label: Screenshot/ Video + description: If applicable, add screenshots to help explain your problem. + validations: + required: false + - type: textarea + attributes: + label: Additional context + description: Add any other context about the problem here. + validations: + required: false \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..13f3339 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Discord community support + url: https://unkey.dev/discord + about: Need any help? Found any bug? Please chat with us via Discord. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/doc_report.yml b/.github/ISSUE_TEMPLATE/doc_report.yml new file mode 100644 index 0000000..50131da --- /dev/null +++ b/.github/ISSUE_TEMPLATE/doc_report.yml @@ -0,0 +1,18 @@ +name: Documentation request +description: Change regarding improving the docs to be more accessible +title: '[docs]' +labels: ['documentation'] +body: + - type: textarea + attributes: + label: Category of documentation update + description: | + What category does this change fall under.For example Typo error, New category addition, Rephrasing the sentences, fixing broken links etc + validations: + required: true + - type: textarea + attributes: + label: Describe the change you think might work + description: Please describe the change & need of change in atmost detail possible. + validations: + required: false \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..84321bc --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,24 @@ +name: Feature request +description: Suggest an idea for this project +title: '[feat]' +labels: ['enhancement'] +body: + - type: textarea + attributes: + label: Would you like to have a new feature? Please describe. + description: A clear and concise description of what the feature is + placeholder: Ex. Whenever I try to do (xyz) then [...] + validations: + required: false + - type: textarea + attributes: + label: How would this feature be beneficial for the project + description: A clear and concise description about the importance of the feature + validations: + required: false + - type: textarea + attributes: + label: Additional context + description: Add any other context or screenshots about the feature request here. + validations: + required: false \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/others.yml b/.github/ISSUE_TEMPLATE/others.yml new file mode 100644 index 0000000..9d0b962 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/others.yml @@ -0,0 +1,22 @@ +name: Other +description: Use this for any other issues. PLEASE do not create blank issues +title: "[other]" +labels: ["random"] +body: + - type: markdown + attributes: + value: "# Other issue" + - type: textarea + id: issuedescription + attributes: + label: What would you like to share? + description: Provide a clear and concise explanation of your issue. + validations: + required: true + - type: textarea + id: extrainfo + attributes: + label: Additional information + description: Is there anything else we should know about this issue? + validations: + required: false \ No newline at end of file diff --git a/.github/pull_request_template.yml b/.github/pull_request_template.yml new file mode 100644 index 0000000..0f3c345 --- /dev/null +++ b/.github/pull_request_template.yml @@ -0,0 +1,32 @@ + + +## Fixes Issue + +This PR fixes the following issues: + +#example + + +## Changes proposed + +Here comes all the changes proposed through this PR + + + + +## Check List (Check all the boxes which are applicable) + +- [ ] My code follows the code style of this project. +- [ ] My change requires a change to the documentation. +- [ ] I have updated the documentation accordingly. +- [ ] All new and existing tests passed. +- [ ] This PR does not contain plagiarized content. +- [ ] The title of my pull request is a short description of the requested changes. + + + +## Screenshots +Add all the screenshots which support your changes \ No newline at end of file diff --git a/.github/workflows/greetings.yml b/.github/workflows/greetings.yml new file mode 100644 index 0000000..ffe5e99 --- /dev/null +++ b/.github/workflows/greetings.yml @@ -0,0 +1,15 @@ +name: Greetings + +on: [pull_request_target, issues] + +jobs: + welcome: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - uses: EddieHubCommunity/gh-action-community/src/welcome@main + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + issue-message: '

Hello 👋 Thank you very much for raising an issue 🙌 The maintainers will get back to you soon for discussion over the issue! Thank you for your contributions. 🚀

' + pr-message: '

Yeah! You did it 🎉 Now, Relax 😉 -> Grab a drink ☕ -> And wait for the maintainers to check your contributions. Meanwhile, you can discuss on other issues and solve them 😀

' + footer: 'Meanwhile you can discuss about the project in our Discord Server 😀' \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..5d2ff78 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,29 @@ +name: Changelog +on: + push: + branches: + - main + +jobs: + changelog: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Conventional Changelog Action + id: changelog + uses: TriPSs/conventional-changelog-action@v3 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + output-file: "false" + + - name: Create Release + uses: actions/create-release@v1 + if: ${{ steps.changelog.outputs.skipped == 'false' }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ steps.changelog.outputs.tag }} + release_name: ${{ steps.changelog.outputs.tag }} + body: ${{ steps.changelog.outputs.clean_changelog }} \ No newline at end of file diff --git a/README.md b/README.md index 02f633d..39147e6 100644 --- a/README.md +++ b/README.md @@ -128,6 +128,48 @@ func main() { ``` > Note: The API returns `{}` as a success response for revkoking key. For user convenience, unkey-go returns boolean +**Get API Information** +```go +package main + +import ( + "fmt" + + unkey "github.com/WilfredAlmeida/unkey-go/features" +) + +func main() { + response, err := features.APIGet("API_ID","AUTH_TOKEN") + if err != nil { + fmt.Println("Error:", err) + return + } + + fmt.Printf("Response: %+v\n", response) +} +``` + +**Get List Keys** +```go +package main + +import ( + "fmt" + + unkey "github.com/WilfredAlmeida/unkey-go/features" +) + +func main() { + response, err := features.APIListKeys("API_ID","AUTH_TOKEN") + if err != nil { + fmt.Println("Error:", err) + return + } + + fmt.Printf("Response: %+v\n", response) +} +``` + --- ### Structs Reference The structs used in code for you to get a better idea of the request & response bodies. @@ -179,6 +221,34 @@ type KeyCreateResponse struct { } ``` +**APIGet** +```go +type APIGetResponse struct { + ID string `json:"id"` + Name string `json:"name"` + WorkspaceID string `json:"workspaceId"` +} +``` + +**APIListKeys** +```go +type Key struct { + ID string `json:"id,omitempty"` + APIID string `json:"apiId,omitempty"` + WorkspaceID string `json:"workspaceId,omitempty"` + Start string `json:"start,omitempty"` + CreatedAt int64 `json:"createdAt,omitempty"` + Expires *int64 `json:"expires,omitempty"` + Ratelimit ratelimitResponse `json:"ratelimit,omitempty"` +} + +type APIListKeysResponse struct { + Keys []Key `json:"keys,omitempty"` + Total int `json:"total,omitempty"` +} +``` +> Note: `ratelimitResponse` is defined in `KeyVerify` struct reference + --- ## Contributing Refer the [CONTRIBUTING.md](https://github.com/WilfredAlmeida/unkey-go/blob/main/CONTRIBUTING.md) file for more information. \ No newline at end of file diff --git a/features/api_get.go b/features/api_get.go new file mode 100644 index 0000000..5c7784e --- /dev/null +++ b/features/api_get.go @@ -0,0 +1,58 @@ +package features + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + + "github.com/WilfredAlmeida/unkey-go/utils" +) + +type APIGetResponse struct { + ID string `json:"id"` + Name string `json:"name"` + WorkspaceID string `json:"workspaceId"` +} + +func APIGet(apiID, authToken string) (APIGetResponse, error) { + + // Create a new HTTP client + client := &http.Client{} + + // Create a new GET request + req, err := http.NewRequest("GET", utils.UNKEY_API_URL+"/apis/"+apiID, nil) + if err != nil { + return APIGetResponse{}, err + } + + // Set the authorization header + req.Header.Set("Authorization", authToken) + + // Send the request + resp, err := client.Do(req) + if err != nil { + return APIGetResponse{}, err + } + defer resp.Body.Close() + + // Read the response body + body, err := io.ReadAll(resp.Body) + if err != nil { + return APIGetResponse{}, err + } + + // Check the response status code + if resp.StatusCode != http.StatusOK { + return APIGetResponse{}, fmt.Errorf(string(body)) + } + + // Parse the response JSON + var response APIGetResponse + err = json.Unmarshal(body, &response) + if err != nil { + return APIGetResponse{}, err + } + + return response, nil +} diff --git a/features/api_get_test.go b/features/api_get_test.go new file mode 100644 index 0000000..ccd2999 --- /dev/null +++ b/features/api_get_test.go @@ -0,0 +1,72 @@ +package features + +import ( + "errors" + "os" + "reflect" + "testing" + + "github.com/joho/godotenv" +) + +func TestAPIGet(t *testing.T) { + + err := godotenv.Load("../.env") + + if err != nil { + t.Errorf("Error loading .env file") + } + + testCases := []struct { + name string + apiId string + authToken string + expectedResult APIGetResponse + expectedError error + }{ + { + name: "Successful Response", + apiId: os.Getenv("API_ID"), + authToken: os.Getenv("AUTH_TOKEN"), + expectedResult: APIGetResponse{ + ID: os.Getenv("API_ID"), + Name: "first-api", + WorkspaceID: "ws_C4EkWVE5UFjG4gdZjKJ9wu", + }, + expectedError: nil, + }, + { + name: "Error Response", + apiId: "someId", + authToken: os.Getenv("AUTH_TOKEN"), + expectedResult: APIGetResponse{}, + expectedError: errors.New(`{"error":"unable to find api: someId","code":"NOT_FOUND"}`), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + + response, err := APIGet(tc.apiId, tc.authToken) + + if err != nil && tc.expectedError == nil { + t.Errorf("Unexpected error: %v", err) + return + } + if err == nil && tc.expectedError != nil { + t.Errorf("Expected error: %v, got nil", tc.expectedError) + return + } + if err != nil && tc.expectedError != nil && err.Error() != tc.expectedError.Error() { + t.Errorf("Expected error: %v, got: %v", tc.expectedError, err) + return + } + + if !reflect.DeepEqual(response, tc.expectedResult) { + t.Errorf("Expected response: %v, got: %v", tc.expectedResult, response) + return + } + + }) + } +} diff --git a/features/api_list_keys.go b/features/api_list_keys.go new file mode 100644 index 0000000..8587f8d --- /dev/null +++ b/features/api_list_keys.go @@ -0,0 +1,67 @@ +package features + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + + "github.com/WilfredAlmeida/unkey-go/utils" +) + +type Key struct { + ID string `json:"id,omitempty"` + APIID string `json:"apiId,omitempty"` + WorkspaceID string `json:"workspaceId,omitempty"` + Start string `json:"start,omitempty"` + CreatedAt int64 `json:"createdAt,omitempty"` + Expires *int64 `json:"expires,omitempty"` + Ratelimit ratelimitResponse `json:"ratelimit,omitempty"` +} + +type APIListKeysResponse struct { + Keys []Key `json:"keys,omitempty"` + Total int `json:"total,omitempty"` +} + +func APIListKeys(apiID, authToken string) (APIListKeysResponse, error) { + + // Create a new HTTP client + client := &http.Client{} + + // Create a new GET request + req, err := http.NewRequest("GET", utils.UNKEY_API_URL+"/apis/"+apiID+"/keys", nil) + if err != nil { + return APIListKeysResponse{}, err + } + + // Set the authorization header + req.Header.Set("Authorization", authToken) + + // Send the request + resp, err := client.Do(req) + if err != nil { + return APIListKeysResponse{}, err + } + defer resp.Body.Close() + + // Read the response body + body, err := io.ReadAll(resp.Body) + if err != nil { + return APIListKeysResponse{}, err + } + + // Check the response status code + if resp.StatusCode != http.StatusOK { + return APIListKeysResponse{}, fmt.Errorf(string(body)) + } + + // Parse the response JSON + var response APIListKeysResponse + err = json.Unmarshal(body, &response) + if err != nil { + return APIListKeysResponse{}, err + } + + return response, nil +} diff --git a/features/api_list_keys_test.go b/features/api_list_keys_test.go new file mode 100644 index 0000000..583fed4 --- /dev/null +++ b/features/api_list_keys_test.go @@ -0,0 +1,64 @@ +package features + +import ( + "errors" + "os" + "testing" + + "github.com/joho/godotenv" +) + +func TestAPIListKeys(t *testing.T) { + + err := godotenv.Load("../.env") + + if err != nil { + t.Errorf("Error loading .env file") + } + + testCases := []struct { + name string + apiId string + authToken string + expectedResult APIListKeysResponse + expectedError error + }{ + { + name: "Successful Response", + apiId: os.Getenv("API_ID"), + authToken: os.Getenv("AUTH_TOKEN"), + expectedResult: APIListKeysResponse{}, // It'll list lots of keys which we cannot match here + expectedError: nil, + }, + { + name: "Error Response", + apiId: "someId", + authToken: os.Getenv("AUTH_TOKEN"), + expectedResult: APIListKeysResponse{}, + expectedError: errors.New(`{"error":"unable to find api: someId","code":"NOT_FOUND"}`), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + + _, err := APIListKeys(tc.apiId, tc.authToken) + + if err != nil && tc.expectedError == nil { + t.Errorf("Unexpected error: %v", err) + return + } + if err == nil && tc.expectedError != nil { + t.Errorf("Expected error: %v, got nil", tc.expectedError) + return + } + if err != nil && tc.expectedError != nil && err.Error() != tc.expectedError.Error() { + t.Errorf("Expected error: %v, got: %v", tc.expectedError, err) + return + } + + // If there's no error then it means we got a successful response + + }) + } +} diff --git a/main.go b/main.go index b90370c..1d55d07 100644 --- a/main.go +++ b/main.go @@ -2,8 +2,10 @@ package main import ( "fmt" + "os" "github.com/WilfredAlmeida/unkey-go/features" + "github.com/joho/godotenv" ) func main() { @@ -46,12 +48,34 @@ func main() { // fmt.Println("Response: ", response) - response, err := features.KeyVerify("key_3ZZSawUTYL1DdsgCycdp7Xdu") + // response, err := features.KeyVerify("key_3ZZSawUTYL1DdsgCycdp7Xdu") + // if err != nil { + // fmt.Println("Error:", err) + // return + // } + + // fmt.Printf("Response.ratelimit: %+v\n", response.Ratelimit) + + err := godotenv.Load("./.env") + + if err != nil { + fmt.Println("Error loading .env file") + fmt.Println(err) + } + + // response, err := features.APIGet(os.Getenv("API_ID"),os.Getenv("AUTH_TOKEN")) + // if err != nil { + // fmt.Println("Error:", err) + // return + // } + + // fmt.Printf("Response: %+v\n", response) + response, err := features.APIListKeys(os.Getenv("API_ID"), os.Getenv("AUTH_TOKEN")) if err != nil { fmt.Println("Error:", err) return } - fmt.Printf("Response.ratelimit: %+v\n", response.Ratelimit) + fmt.Printf("Response: %+v\n", response) } diff --git a/utils/constants.go b/utils/constants.go index 78ca125..3ee90b1 100644 --- a/utils/constants.go +++ b/utils/constants.go @@ -2,4 +2,4 @@ package utils const UNKEY_API_VERSION = "v1" const UNKEY_BASE_URL = "https://api.unkey.dev" -const UNKEY_API_URL = UNKEY_BASE_URL + "/" + UNKEY_API_VERSION \ No newline at end of file +const UNKEY_API_URL = UNKEY_BASE_URL + "/" + UNKEY_API_VERSION