diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000..4a36c32 --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,18 @@ +name: Run tests +run-name: ${{ github.event.pull_request.title }} (#${{ github.event.pull_request.number }}) - Run tests +on: + pull_request: + types: [opened, synchronize, reopened] + push: + branches: + - master +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-go@v4 + with: + go-version: '1.20.2' + - name: Test + run: make test \ No newline at end of file diff --git a/.gitignore b/.gitignore index b5d7bd7..d48c759 100644 --- a/.gitignore +++ b/.gitignore @@ -1,90 +1,2 @@ -### JetBrains template -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 - -# User-specific stuff -.idea/**/workspace.xml -.idea/**/tasks.xml -.idea/**/usage.statistics.xml -.idea/**/dictionaries -.idea/**/shelf - -# Generated files -.idea/**/contentModel.xml - -# Sensitive or high-churn files -.idea/**/dataSources/ -.idea/**/dataSources.ids -.idea/**/dataSources.local.xml -.idea/**/sqlDataSources.xml -.idea/**/dynamic.xml -.idea/**/uiDesigner.xml -.idea/**/dbnavigator.xml - -# Gradle -.idea/**/gradle.xml -.idea/**/libraries - -# Gradle and Maven with auto-import -# When using Gradle or Maven with auto-import, you should exclude module files, -# since they will be recreated, and may cause churn. Uncomment if using -# auto-import. -# .idea/artifacts -# .idea/compiler.xml -# .idea/jarRepositories.xml -# .idea/modules.xml -# .idea/*.iml -# .idea/modules -# *.iml -# *.ipr - -# CMake -cmake-build-*/ - -# Mongo Explorer plugin -.idea/**/mongoSettings.xml - -# File-based project format -*.iws - -# IntelliJ -out/ - -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Cursive Clojure plugin -.idea/replstate.xml - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties - -# Editor-based Rest Client -.idea/httpRequests - -# Android studio 3.1+ serialized cache file -.idea/caches/build_file_checksums.ser - -### Go template -# Binaries for programs and plugins -*.exe -*.exe~ -*.dll -*.so -*.dylib - -# Test binary, built with `go test -c` -*.test - -# Output of the go coverage tool, specifically when used with LiteIDE -*.out - -# Dependency directories (remove the comment below to include it) -# vendor/ - +.idea +.vscode \ No newline at end of file diff --git a/LICENSE b/LICENSE index 02e3889..261eeb9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,201 @@ -MIT License - -Copyright (c) 2022 5GCoreNet - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d4231d4 --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ +GOPATH := $(shell go env GOPATH) + +dependencies: + echo "Installing dependencies" + go install github.com/golang/mock/mockgen@v1.6.0 + go mod download + +mock-gen: dependencies + echo "Generating mock files" + PATH=$(PATH):$(GOPATH)/bin go generate ./... + +test: mock-gen + echo "Running tests" + go test -v ./... + +.PHONY: mock-gen diff --git a/README.md b/README.md index 59011a6..62b2e15 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,26 @@ # 5GCoreNetSDK -[![GitHub go.mod Go version of a Go module](https://img.shields.io/github/go-mod/go-version/5GCoreNet/5GCoreNetSDK.svg)](https://github.com/5GCoreNet/5GCoreNetSDK) -[![GitHub license](https://badgen.net/github/license/5GCoreNet/5GCoreNetSDK)](https://github.com/5GCoreNet/5GCoreNetSDK/blob/main/LICENSE) -[![GitHub stars](https://img.shields.io/github/stars/5GCoreNet/5GCoreNetSDK.svg?style=social&label=Star&maxAge=2592000)](https://github.com/5GCoreNet/5GCoreNetSDK) +[![Go Reference](https://pkg.go.dev/badge/github.com/5GCoreNet/5GCoreNetSDK.svg)](https://pkg.go.dev/github.com/5GCoreNet/5GCoreNetSDK) +[![Go Report Card](https://goreportcard.com/badge/github.com/5GCoreNet/5GCoreNetSDK)](https://goreportcard.com/report/github.com/5GCoreNet/5GCoreNetSDK) +[![License](https://img.shields.io/github/license/5GCoreNet/5GCoreNetSDK)](LICENSE) +![GitHub stars](https://img.shields.io/github/stars/5GCoreNet/5GCoreNetSDK?style=social) +> At this moment, this SDK is in development. It is not ready for production use. +> Refers to the [Roadmap](#roadmap) section for more information on what has been done. +> +> See the [Contributing](#contributing) section if you would like to help make it better. +

- +

-> At this moment, this SDK is in development. It is not ready for production use. -> Refers to the [Roadmap](#roadmap) section for more information on what has been done. -> -> See the [Contributing](#contributing) section if you would like to help -> make it better. - -5GCoreNetSDK is an open source project that provides a set of APIs to access or provide services in 5G Core Network. The APIs are based on the 3GPP specifications and are implemented in Golang. +5GCoreNetSDK is an open source project that provides a set of APIs to access or expose a Network Function (NF) in 5G Core Network. +The APIs are based on the 3GPP specifications and are implemented in Golang. -Under the hood, the SDK exposes through a RESTful API the Network Function you've built, according to the standard. The RESTful API is implemented using [Gin](https://github.com/gin-gonic/gin) framework. +Under the hood, the SDK exposes the standard HTTP RESTful API for the Network Function you've built. +The RESTful APIs are implemented using [Gin](https://github.com/gin-gonic/gin) framework. -At the moment, the APIs are implemented for the release 18 of the 3GPP specifications. +The SDK follows the R18 3GPP specifications. ## Getting Started @@ -26,19 +28,37 @@ At the moment, the APIs are implemented for the release 18 of the 3GPP specifica go get github.com/5GCoreNet/5GCoreNetSDK ``` +## Features -## Contributing -Feel free to contribute to this project. You can do it by: -- Reporting bugs -- Suggesting new features -- Implementing new features -- Fixing bugs -- Improving documentation -- ... - -## License -Licensed under the MIT License. See the [LICENSE](LICENSE) file for details. +- Simple and easy to use, just implement the interface you need, and you are ready to go +- Support for both Server and Client mode +- Follows the 3GPP specifications out of the box (R18) +- C compatible thanks to [cgo](https://golang.org/cmd/cgo/) +- Fully open source and free to use, modify, and distribute under the terms of the [Apache 2.0 license](LICENSE) ## Roadmap -TBD +As 5GCoreNetSDK is still in development, the following table is the roadmap representing what has been done and what is still to do. +The following table shows the status of the APIs and the order in which they will be implemented. If you want to see a specific API implemented, +you can open an issue (or you can implement it and open a pull request). + +Network Function | API | Status | Comments | Documentation +---------------- |------|-----------------|-----------------------------------------------------------------------------------------| ------------- +LMF | NLMF | In progress | NLMF is the first API proposal and is considered as a PoC. NLMF might change in future. | [Link](fivegc/nlmf/examples/main.go) +NRF | NNRF | Not implemented | | +AMF | NAMF | Not implemented | | +SMF | NSMF | Not implemented | | +UDM | NUDM | Not implemented | | +UDR | NUDR | Not implemented | | +AUSF | NAUSF | Not implemented | | +PCF | NPCF | Not implemented | | +NSSF | NNSSF | Not implemented | | +N3IWF | NN3IWF | Not implemented | | +UPF | NUPF | Not implemented | | + + +> Note that API is considered implemented if it is implemented in the SDK in Server and Client mode and if it is tested and documented. + +## Contributing + +Contributions are welcome! Please read the [Contributing Guide](docs/CONTRIBUTING.md) for more information. \ No newline at end of file diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md new file mode 100644 index 0000000..c64997a --- /dev/null +++ b/docs/CONTRIBUTING.md @@ -0,0 +1,106 @@ +# Contributing + +When contributing to this repository, please ensure that your Pull Request is linked to an issue, +and that the issue is tagged with the appropriate labels (e.g. bug, enhancement, etc.). This will +help us to keep track of the changes and discuss them. + +Please note we have a code of conduct, please follow it in all your interactions with the project. + +If you want to contribute to 5GCoreNetSDK, please make sure that your code follows principles defined in [Design Principles](design-principles.md). +## Pull Request Process + +Firstly, thank you for contributing to this repository. Please ensure that you gather the following requirements to contribute: + +1. Ensure that the Pull Request is linked to an issue. +2. Ensure that the issue is tagged with the appropriate labels (e.g. bug, enhancement, etc.) and prefix your Pull Request with the domain of the issue and NF impacted (e.g. `bugfix/nlmf`, `enhancement/nlmf`, etc.). +3. Ensure that you only include one commit in your Pull Request. If you have multiple commits, please squash them into one commit. +4. Ensure you sign your commits. See [Signing your commits](#signing-your-commits) for more information. +5. Ensure that you have updated the documentation and the tests to reflect your changes. +6. Run the tests to ensure that your changes do not break any existing tests. +7. Ensure that your code is formatted using `gofmt` and linted using `golint`. +8. Prepare a Pull Request with the following information: + * A description of the changes you have made. + * A link to the issue you are fixing. + +### Signing your commits + +To sign your commits, just add the `-s` flag when you commit. This will add a `Signed-off-by` line to your commit message. For example: + +```bash +git commit -s -m "This is my commit message" +``` + +## Code of Conduct + +### Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +### Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +### Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +### Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +### Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at [INSERT EMAIL ADDRESS]. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +### Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ \ No newline at end of file diff --git a/docs/design-principles.md b/docs/design-principles.md new file mode 100644 index 0000000..387651c --- /dev/null +++ b/docs/design-principles.md @@ -0,0 +1,115 @@ +# Design principals + +As 5GCoreNetSDK aims to be a simple and easy to use SDK, we have defined some design principals that we want to follow. + +If you want to contribute to 5GCoreNetSDK, please make sure that your code follows these principals. + +## Simple and easy to use + +This is the main goal of 5GCoreNetSDK. We want to provide a simple and easy to use SDK to build 5G Core Network NFs following the R18 3GPP specifications. + +Indeed, some 3GPP specifications are very complex and hard to understand. We want to make it easy to use the 3GPP specifications in code. + +## Interface based + +5GCoreNetSDK is based on interfaces. This means that you need to implement the interfaces. To do so, you just need to +create a struct that implements the interface and that's it. + +This is very useful because it allows you to provide your own implementation of the Network Functions without bothering about the 3GPP specifications. + +Plus, it makes it easy to test your code, because you can mock the interfaces you need. + +So every Network Function connectivity layer must be based on an interface. + +Let's take an example, consider we want to implement the `Foo` endpoint. + +```go +package foo + +import ( + "context" + "github.com/5GCore/5GCoreNetSDK/fivegc" +) + +type Foo interface { + fivegc.CommonInterface + Foo(context.Context, string) string, ProblemDetails, fivegc.RedirectResponse, FooStatusCode +} +``` + +This interface is composed of the `CommonInterface` interface, which is the interface that every endpoint must implement. + +`CommonInterface` is here to provide a common interface wrapping error handling (maybe we will add more stuff in the future). + +Then, we have the `Foo` method, which is the method that will be called when a request is sent to the `Foo` endpoint. + +And that's it, you have implemented the `Foo` endpoint. Not so hard, right? + +## Client flow +Interfaces are used for the server part, whereas the client part is following a more classical flow. + +Client must follow the following flow to send a request to a service: + +1 - Create a new client +2 - Create a new query +3 - Fill the query with the parameters +3 - Execute the query + +```go +// Create a new client +client := client.NewClient(cfg) +// Client can be used to create a new query +query := client.NetworkFunctionEndpoint(ctx) +// Query can be used to fill the parameters +query = query.Paramaters(model) +// Query can be used to send a request to the server +response, err := client.NetworkFunctionEndpointExecute(query) +``` + +This flow is very simple and easy to use. It is also very easy to test, because you can mock the client and the query, +thus we decided to follow this flow for the client part. + +## Error handling + +As in 3GPP specifications, errors are handled using the `ProblemDetails` struct. + +Unmarshalling errors must be handled using the `ProblemDetails` struct as well. This struct is used to provide additional information in an error response. +That's why we decided to add na `Error()` method to all the interfaces by using the `CommonInterface` interface. + +```go +type CommonInterface interface { + // Error returns a problem details, it is used to handle errors when unmarshalling the request. + Error(ctx context.Context, err error) openapicommon.ProblemDetails +} +``` +Error is called when an error occurs during the unmarshalling of the request. It is used to provide additional information about the error. + +Thus, SDK users can handle by themselves the errors without effort. + +## Mocking + +Every interface implemented must be mocked. This is a very elegant way to provide user a way to test their code. + +In order to do so, we use the `mockgen` library. This library allows us to generate mocks from interfaces. + +Let's take an example, consider we want to mock the `Foo` interface. + +```go +package mock // located under foo/mock + +//go:generate mockgen -source=../foo.go -destination=foo.go -package=mock +``` + +This command will generate a `foo.go` file under the `foo/mock` package. This file will contain a mocked `Foo` interface. + +## Testing + +Every endpoint must be tested against the 3GPP specifications. Thus, we can ensure that our code is compliant with the 3GPP specifications and that we are not breaking anything. + +## Documentation + +Every endpoint must be documented. This is very important because it allows users to understand how to use the endpoint. + +You have to use GoDoc format to document your code. This is very easy to use, and it is very useful. You can find more information about GoDoc [here](https://blog.golang.org/godoc). + +Plus, you can add an example of how to use the endpoint. This is very useful because it allows users to better understand how to use the endpoint. \ No newline at end of file diff --git a/docs/images/logo_no_bg.png b/docs/images/logo_no_bg.png new file mode 100644 index 0000000..ab8141a Binary files /dev/null and b/docs/images/logo_no_bg.png differ diff --git a/docs/images/logo_white_bg.png b/docs/images/logo_white_bg.png new file mode 100644 index 0000000..a0fa941 Binary files /dev/null and b/docs/images/logo_white_bg.png differ diff --git a/docs/philosophy.md b/docs/philosophy.md new file mode 100644 index 0000000..10abe18 --- /dev/null +++ b/docs/philosophy.md @@ -0,0 +1,47 @@ +# Philosophy of 5GCoreNetSDK + +This document describes the philosophy of 5GCoreNetSDK and why we decided to start this project. + +Feel free to contribute to this document by creating a pull request or by opening an issue. + +## Motivation +5G Core Network is a complex network. It is composed of many Network Functions (NFs) that communicate with each other. +The 3GPP specifications are very complex and hard to understand. Thus, it is hard to develop inside this network. + +Many projects provide already built Network Functions (NFs) that you can use, but if you want to build your own Network Function (NF), +you need to implement the 3GPP specifications by yourself. This is very time-consuming and hard to do. + +Through this project, we want to provide a simple and easy to use SDK to build 5G Core Network NFs following the R18 3GPP specifications. + +The SDK is designed to be simple and easy to use, just implement the interface you need, and you are ready to go. + + +## What 5GCoreNetSDK is + +5GCoreNetSDK is a developer centric SDK to build 5G Core Network Functions (NFs) following the R18 3GPP specifications. + +It is written in [Golang](#why-golang) and is based on interfaces. This means that you only need to implement the interfaces you need to connect your Network Function (NF) to the 5G Core Network. + +Plus, thanks to the interfaces, even if you don't know the 3GPP specifications nor 5G Core design, you can easily develop a Network Function (NF) that will work with the 5G Core Network. + +## What 5GCoreNetSDK is not + +5GCoreNetSDK is not a : + +* Network Function (NF) implementation. +* 5G Core Network implementation. +* 5G Core Network simulator. +* 5G Core Network emulator. + +The goal of 5GCoreNetSDK is to provide a simply to use connectivity layer inside the 5G Core Network. + +## Technical choices + +### Why Golang? + +As you may know, many Network Functions (NFs) are implemented in C. This is because C is a very fast and secure language. +However, C is not easy to use. It is not easy to learn, and it is not easy to write code in C making it hard to maintain. + +Projects like [Free5GC](https://www.free5gc.org/) decided to use Golang to implement their Network Functions (NFs). This is because Golang is a modern programming language that is easy to learn and use. It is also a compiled language, which means that it is fast and +secure. Golang is also a C compatible language, which means that you can use C libraries in your Golang code, thus +you can easily import your C code to use it with 5GCoreNetSDK. diff --git a/fivegc/helper.go b/fivegc/helper.go new file mode 100644 index 0000000..78a5fb9 --- /dev/null +++ b/fivegc/helper.go @@ -0,0 +1,33 @@ +package fivegc + +func ToInt(i int) *int { + return &i +} + +func ToInt64(i int64) *int64 { + return &i +} + +func ToInt32(i int32) *int32 { + return &i +} + +func ToInt16(i int16) *int16 { + return &i +} + +func ToInt8(i int8) *int8 { + return &i +} + +func ToFloat64(f float64) *float64 { + return &f +} + +func ToFloat32(f float32) *float32 { + return &f +} + +func ToString(s string) *string { + return &s +} diff --git a/fivegc/nlmf/broadcast.go b/fivegc/nlmf/broadcast.go index d1f0cac..38e9a0c 100644 --- a/fivegc/nlmf/broadcast.go +++ b/fivegc/nlmf/broadcast.go @@ -4,71 +4,84 @@ import ( "context" "github.com/5GCoreNet/5GCoreNetSDK/fivegc" "github.com/5GCoreNet/5GCoreNetSDK/internal/header" - openapinlmfbroadcastclient "github.com/5GCoreNet/client-openapi/Nlmf_Broadcast" - openapinlmfbroadcastserver "github.com/5GCoreNet/server-openapi/Nlmf_Broadcast" + openapicommon "github.com/5GCoreNet/openapi/openapi_CommonData" + openapinlmfbroadcast "github.com/5GCoreNet/openapi/openapi_Nlmf_Broadcast" "github.com/gin-gonic/gin" ) +const ( + broadcastRouterGroup = "/nlmf-broadcast/v1" + cypherKeyEndpoint = "/cipher-key-data" +) + +// Broadcast is the interface that wraps the NLMF Broadcast service. type Broadcast interface { - // Error returns a problem details, it is used to handle errors when unmarshalling the request. - Error(ctx context.Context, err error) openapinlmfbroadcastserver.ProblemDetails - // CipherKeyData returns a cipher response data, a problem details, a redirect response and a status code. - CipherKeyData(context.Context, openapinlmfbroadcastserver.CipherRequestData) (openapinlmfbroadcastserver.CipherResponseData, openapinlmfbroadcastserver.ProblemDetails, fivegc.RedirectResponse, fivegc.StatusCode) + fivegc.CommonInterface + CipherKeyData(context.Context, openapinlmfbroadcast.CipherRequestData) (openapinlmfbroadcast.CipherResponseData, openapicommon.ProblemDetails, fivegc.RedirectResponse, CypherResponseStatusCode) } +type CypherResponseStatusCode fivegc.StatusCode + +const ( + // CypherResponseStatusCodeOK is the status code for a successful response. + CypherResponseStatusCodeOK CypherResponseStatusCode = CypherResponseStatusCode(fivegc.StatusOK) + CypherResponseStatusTemporaryRedirect CypherResponseStatusCode = CypherResponseStatusCode(fivegc.StatusTemporaryRedirect) + CypherResponseStatusPermanentRedirect CypherResponseStatusCode = CypherResponseStatusCode(fivegc.StatusPermanentRedirect) +) + func attachBroadcastHandler(router *gin.RouterGroup, b Broadcast) { - group := router.Group("/nlmf-broadcast/v1") + group := router.Group(broadcastRouterGroup) { - group.POST("/cipher-key-data", func(c *gin.Context) { - var req openapinlmfbroadcastserver.CipherRequestData + group.POST(cypherKeyEndpoint, func(c *gin.Context) { + var req openapinlmfbroadcast.CipherRequestData if err := c.ShouldBindJSON(&req); err != nil { problemDetails := b.Error(c, err) - c.JSON(int(problemDetails.Status), problemDetails) + c.JSON(int(*problemDetails.Status), problemDetails) return } res, problemDetails, redirectResponse, status := b.CipherKeyData(c, req) switch status { - case fivegc.StatusOK: - c.JSON(status.ToInt(), res) - case fivegc.StatusTemporaryRedirect: + case CypherResponseStatusCodeOK: + c.JSON(int(status), res) + case CypherResponseStatusTemporaryRedirect: header.BindRedirectHeader(c, redirectResponse.RedirectHeader) - c.JSON(status.ToInt(), redirectResponse) - case fivegc.StatusPermanentRedirect: + c.JSON(int(status), redirectResponse) + case CypherResponseStatusPermanentRedirect: header.BindRedirectHeader(c, redirectResponse.RedirectHeader) - c.JSON(status.ToInt(), redirectResponse) + c.JSON(int(status), redirectResponse) default: - c.JSON(status.ToInt(), problemDetails) + c.JSON(int(status), problemDetails) } return }) } } -// BroadcastClient is a client for the Nlmf_Broadcast service. +// BroadcastClient is a client for the NLMF Broadcast service. type BroadcastClient struct { - client *openapinlmfbroadcastclient.APIClient + client *openapinlmfbroadcast.APIClient } -// NewBroadcastClient creates a new client for the Nlmf_Broadcast service. +// NewBroadcastClient creates a new client for the NLMF Broadcast service. func NewBroadcastClient(cfg fivegc.ClientConfiguration) *BroadcastClient { - openapiCfg := &openapinlmfbroadcastclient.Configuration{ + openapiCfg := &openapinlmfbroadcast.Configuration{ Host: cfg.Host, Scheme: cfg.Scheme, DefaultHeader: cfg.DefaultHeader, UserAgent: cfg.UserAgent, Debug: cfg.Debug, - Servers: []openapinlmfbroadcastclient.ServerConfiguration{}, - OperationServers: make(map[string]openapinlmfbroadcastclient.ServerConfigurations), + Servers: []openapinlmfbroadcast.ServerConfiguration{}, + OperationServers: make(map[string]openapinlmfbroadcast.ServerConfigurations), HTTPClient: cfg.HTTPClient, } for _, server := range cfg.Servers { - openapiServer := openapinlmfbroadcastclient.ServerConfiguration{ + openapiServer := openapinlmfbroadcast.ServerConfiguration{ URL: server.URL, Description: server.Description, - Variables: make(map[string]openapinlmfbroadcastclient.ServerVariable), + Variables: make(map[string]openapinlmfbroadcast.ServerVariable), } for name, variable := range server.Variables { - openapiServer.Variables[name] = openapinlmfbroadcastclient.ServerVariable{ + openapiServer.Variables[name] = openapinlmfbroadcast.ServerVariable{ Description: variable.Description, DefaultValue: variable.DefaultValue, EnumValues: variable.EnumValues, @@ -77,15 +90,15 @@ func NewBroadcastClient(cfg fivegc.ClientConfiguration) *BroadcastClient { openapiCfg.Servers = append(openapiCfg.Servers, openapiServer) } for name, servers := range cfg.OperationServers { - openapiServers := make(openapinlmfbroadcastclient.ServerConfigurations, len(servers)) + openapiServers := make(openapinlmfbroadcast.ServerConfigurations, len(servers)) for i, server := range servers { - openapiServers[i] = openapinlmfbroadcastclient.ServerConfiguration{ + openapiServers[i] = openapinlmfbroadcast.ServerConfiguration{ URL: server.URL, Description: server.Description, - Variables: make(map[string]openapinlmfbroadcastclient.ServerVariable), + Variables: make(map[string]openapinlmfbroadcast.ServerVariable), } for name, variable := range server.Variables { - openapiServers[i].Variables[name] = openapinlmfbroadcastclient.ServerVariable{ + openapiServers[i].Variables[name] = openapinlmfbroadcast.ServerVariable{ Description: variable.Description, DefaultValue: variable.DefaultValue, EnumValues: variable.EnumValues, @@ -95,17 +108,17 @@ func NewBroadcastClient(cfg fivegc.ClientConfiguration) *BroadcastClient { openapiCfg.OperationServers[name] = openapiServers } return &BroadcastClient{ - client: openapinlmfbroadcastclient.NewAPIClient(openapiCfg), + client: openapinlmfbroadcast.NewAPIClient(openapiCfg), } } // CipheringKeyData returns a cipher request. -func (c *BroadcastClient) CipheringKeyData(ctx context.Context) openapinlmfbroadcastclient.ApiCipheringKeyDataRequest { +func (c *BroadcastClient) CipheringKeyData(ctx context.Context) openapinlmfbroadcast.ApiCipheringKeyDataRequest { return c.client.RequestCipheringKeyDataApi.CipheringKeyData(ctx) } // CipheringKeyDataExecute executes a cipher request. -func (c *BroadcastClient) CipheringKeyDataExecute(r openapinlmfbroadcastclient.ApiCipheringKeyDataRequest) (*openapinlmfbroadcastclient.CipherResponseData, error) { +func (c *BroadcastClient) CipheringKeyDataExecute(r openapinlmfbroadcast.ApiCipheringKeyDataRequest) (*openapinlmfbroadcast.CipherResponseData, error) { resp, _, err := r.Execute() return resp, err } diff --git a/fivegc/nlmf/client.go b/fivegc/nlmf/client.go new file mode 100644 index 0000000..dc9b906 --- /dev/null +++ b/fivegc/nlmf/client.go @@ -0,0 +1,16 @@ +package nlmf + +import "github.com/5GCoreNet/5GCoreNetSDK/fivegc" + +type Client struct { + *BroadcastClient + *LocationClient +} + +// NewClient returns a new client for an NLMF service. +func NewClient(config fivegc.ClientConfiguration) *Client { + return &Client{ + BroadcastClient: NewBroadcastClient(config), + LocationClient: NewLocationClient(config), + } +} diff --git a/fivegc/nlmf/examples/main.go b/fivegc/nlmf/examples/main.go new file mode 100644 index 0000000..5d17985 --- /dev/null +++ b/fivegc/nlmf/examples/main.go @@ -0,0 +1,43 @@ +package main + +import ( + "context" + "github.com/5GCoreNet/5GCoreNetSDK/fivegc" + "github.com/5GCoreNet/5GCoreNetSDK/fivegc/nlmf" + openapicommon "github.com/5GCoreNet/openapi/openapi_CommonData" + nlmfbroadcast "github.com/5GCoreNet/openapi/openapi_Nlmf_Broadcast" + "log" +) + +type MyBroadcast struct { +} + +func (m MyBroadcast) Error(ctx context.Context, err error) openapicommon.ProblemDetails { + return openapicommon.ProblemDetails{ + Type: fivegc.ToString("error"), + Title: fivegc.ToString("error"), + Status: fivegc.ToInt32(int32(fivegc.StatusInternalServerError)), + Detail: fivegc.ToString(err.Error()), + Instance: fivegc.ToString("fake_instance"), + Cause: fivegc.ToString("unknown"), + InvalidParams: nil, + SupportedFeatures: fivegc.ToString(""), + AccessTokenError: &openapicommon.AccessTokenErr{}, + AccessTokenRequest: &openapicommon.AccessTokenReq{}, + NrfId: fivegc.ToString("1234567890"), + } +} + +func (m MyBroadcast) CipherKeyData(ctx context.Context, data nlmfbroadcast.CipherRequestData) (nlmfbroadcast.CipherResponseData, openapicommon.ProblemDetails, fivegc.RedirectResponse, nlmf.CypherResponseStatusCode) { + // Your code here ... + return nlmfbroadcast.CipherResponseData{}, openapicommon.ProblemDetails{}, fivegc.RedirectResponse{}, nlmf.CypherResponseStatusCodeOK +} + +func main() { + m := MyBroadcast{} + nlmfServer := nlmf.NewServer(":8080", "/v1/", log.Default()) + nlmfServer.AttachBroadcast(m) + nlmfServer.Start() + // Your code here ... + nlmfServer.Stop() +} diff --git a/fivegc/nlmf/location.go b/fivegc/nlmf/location.go index afe05b2..71edc12 100644 --- a/fivegc/nlmf/location.go +++ b/fivegc/nlmf/location.go @@ -4,122 +4,156 @@ import ( "context" "github.com/5GCoreNet/5GCoreNetSDK/fivegc" "github.com/5GCoreNet/5GCoreNetSDK/internal/header" - openapinlmflocationclient "github.com/5GCoreNet/client-openapi/Nlmf_Location" - openapinlmflocationserver "github.com/5GCoreNet/server-openapi/Nlmf_Location" + openapicommon "github.com/5GCoreNet/openapi/openapi_CommonData" + nlmfocation "github.com/5GCoreNet/openapi/openapi_Nlmf_Location" "github.com/gin-gonic/gin" "net/http" ) +const ( + locationRouterGroup = "/nlmf-loc/v1" + cancelLocationEndpoint = "/cancel-location" + determineLocationEndpoint = "/determine-location" + locationContextTransferEndpoint = "/location-context-transfer" +) + type Location interface { - // Error returns a problem details, it is used to handle errors when unmarshalling the request. - Error(ctx context.Context, err error) openapinlmflocationserver.ProblemDetails + fivegc.CommonInterface // CancelLocation cancels a location request. - CancelLocation(context.Context, openapinlmflocationserver.CancelLocData) (openapinlmflocationserver.ProblemDetails, fivegc.RedirectResponse, fivegc.StatusCode) + CancelLocation(context.Context, nlmfocation.CancelLocData) (openapicommon.ProblemDetails, fivegc.RedirectResponse, CancelLocationStatusCode) // DetermineLocation determines the location of a UE. - DetermineLocation(context.Context, openapinlmflocationserver.InputData) (openapinlmflocationserver.LocationData, openapinlmflocationserver.ProblemDetails, fivegc.RedirectResponse, fivegc.StatusCode) + DetermineLocation(context.Context, nlmfocation.InputData) (nlmfocation.LocationData, openapicommon.ProblemDetails, fivegc.RedirectResponse, DetermineLocationStatusCode) // LocationContextTransfer transfers the location context of a UE. - LocationContextTransfer(context.Context, openapinlmflocationserver.LocContextData) (openapinlmflocationserver.ProblemDetails, fivegc.RedirectResponse, fivegc.StatusCode) + LocationContextTransfer(context.Context, nlmfocation.LocContextData) (openapicommon.ProblemDetails, fivegc.RedirectResponse, LocationContextTransferStatusCode) } +type CancelLocationStatusCode fivegc.StatusCode + +const ( + // CancelLocationStatusNoContent is the status code for the response when the location request is successfully cancelled. + CancelLocationStatusNoContent CancelLocationStatusCode = CancelLocationStatusCode(fivegc.StatusNoContent) + CancelLocationStatusTemporaryRedirect CancelLocationStatusCode = CancelLocationStatusCode(fivegc.StatusTemporaryRedirect) + CancelLocationStatusPermanentRedirect CancelLocationStatusCode = CancelLocationStatusCode(fivegc.StatusPermanentRedirect) +) + +type DetermineLocationStatusCode fivegc.StatusCode + +const ( + // DetermineLocationStatusOK is the status code for a successful response. + DetermineLocationStatusOK DetermineLocationStatusCode = DetermineLocationStatusCode(fivegc.StatusOK) + DetermineLocationStatusNoContent DetermineLocationStatusCode = DetermineLocationStatusCode(fivegc.StatusNoContent) + DetermineLocationStatusTemporaryRedirect DetermineLocationStatusCode = DetermineLocationStatusCode(fivegc.StatusTemporaryRedirect) + DetermineLocationStatusPermanentRedirect DetermineLocationStatusCode = DetermineLocationStatusCode(fivegc.StatusPermanentRedirect) +) + +type LocationContextTransferStatusCode fivegc.StatusCode + +const ( + // LocationContextTransferStatusNoContent is the status code for the response when the location context transfer is successful. + LocationContextTransferStatusNoContent LocationContextTransferStatusCode = LocationContextTransferStatusCode(fivegc.StatusNoContent) + LocationContextTransferStatusTemporaryRedirect LocationContextTransferStatusCode = LocationContextTransferStatusCode(fivegc.StatusTemporaryRedirect) + LocationContextTransferStatusPermanentRedirect LocationContextTransferStatusCode = LocationContextTransferStatusCode(fivegc.StatusPermanentRedirect) +) + func attachLocationHandler(router *gin.RouterGroup, l Location) { - group := router.Group("/nlmf-loc/v1") + group := router.Group(locationRouterGroup) { - group.POST("/cancel-location", func(c *gin.Context) { - var req openapinlmflocationserver.CancelLocData + group.POST(cancelLocationEndpoint, func(c *gin.Context) { + var req nlmfocation.CancelLocData if err := c.ShouldBindJSON(&req); err != nil { problemDetails := l.Error(c, err) - c.JSON(int(problemDetails.Status), problemDetails) + c.JSON(int(*problemDetails.Status), problemDetails) return } problemDetails, redirectResponse, status := l.CancelLocation(c, req) switch status { - case fivegc.StatusNoContent: - c.JSON(status.ToInt(), nil) - case fivegc.StatusTemporaryRedirect: + case CancelLocationStatusNoContent: + c.JSON(int(status), nil) + case CancelLocationStatusTemporaryRedirect: header.BindRedirectHeader(c, redirectResponse.RedirectHeader) - c.JSON(status.ToInt(), redirectResponse) - case fivegc.StatusPermanentRedirect: + c.JSON(int(status), redirectResponse) + case CancelLocationStatusPermanentRedirect: header.BindRedirectHeader(c, redirectResponse.RedirectHeader) - c.JSON(status.ToInt(), redirectResponse) + c.JSON(int(status), redirectResponse) default: - c.JSON(status.ToInt(), problemDetails) + c.JSON(int(status), problemDetails) } return }) - group.POST("/determine-location", func(c *gin.Context) { - var req openapinlmflocationserver.InputData + group.POST(determineLocationEndpoint, func(c *gin.Context) { + var req nlmfocation.InputData if err := c.ShouldBindJSON(&req); err != nil { problemDetails := l.Error(c, err) - c.JSON(int(problemDetails.Status), problemDetails) + c.JSON(int(*problemDetails.Status), problemDetails) return } res, problemDetails, redirectResponse, status := l.DetermineLocation(c, req) switch status { - case fivegc.StatusOK: - c.JSON(status.ToInt(), res) - case fivegc.StatusNoContent: - c.JSON(status.ToInt(), nil) - case fivegc.StatusTemporaryRedirect: + case DetermineLocationStatusOK: + c.JSON(int(status), res) + case DetermineLocationStatusNoContent: + c.JSON(int(status), nil) + case DetermineLocationStatusTemporaryRedirect: header.BindRedirectHeader(c, redirectResponse.RedirectHeader) - c.JSON(status.ToInt(), redirectResponse) - case fivegc.StatusPermanentRedirect: + c.JSON(int(status), redirectResponse) + case DetermineLocationStatusPermanentRedirect: header.BindRedirectHeader(c, redirectResponse.RedirectHeader) - c.JSON(status.ToInt(), redirectResponse) + c.JSON(int(status), redirectResponse) default: - c.JSON(status.ToInt(), problemDetails) + c.JSON(int(status), problemDetails) } return }) - group.POST("/location-context-transfer", func(c *gin.Context) { - var req openapinlmflocationserver.LocContextData + group.POST(locationContextTransferEndpoint, func(c *gin.Context) { + var req nlmfocation.LocContextData if err := c.ShouldBindJSON(&req); err != nil { problemDetails := l.Error(c, err) - c.JSON(int(problemDetails.Status), problemDetails) + c.JSON(int(*problemDetails.Status), problemDetails) return } problemDetails, redirectResponse, status := l.LocationContextTransfer(c, req) switch status { - case fivegc.StatusNoContent: - c.JSON(status.ToInt(), nil) - case fivegc.StatusTemporaryRedirect: + case LocationContextTransferStatusNoContent: + c.JSON(int(status), nil) + case LocationContextTransferStatusTemporaryRedirect: header.BindRedirectHeader(c, redirectResponse.RedirectHeader) - c.JSON(status.ToInt(), redirectResponse) - case fivegc.StatusPermanentRedirect: + c.JSON(int(status), redirectResponse) + case LocationContextTransferStatusPermanentRedirect: header.BindRedirectHeader(c, redirectResponse.RedirectHeader) - c.JSON(status.ToInt(), redirectResponse) + c.JSON(int(status), redirectResponse) default: - c.JSON(status.ToInt(), problemDetails) + c.JSON(int(status), problemDetails) } return }) } } -// LocationClient is a client for the Nlmf_location service. +// LocationClient is a client for the NLMF Location service. type LocationClient struct { - client *openapinlmflocationclient.APIClient + client *nlmfocation.APIClient } -// NewLocationClient creates a new client for the Nlmf_location service. +// NewLocationClient creates a new client for the NLMF Location service. func NewLocationClient(cfg fivegc.ClientConfiguration) *LocationClient { - openapiCfg := &openapinlmflocationclient.Configuration{ + openapiCfg := &nlmfocation.Configuration{ Host: cfg.Host, Scheme: cfg.Scheme, DefaultHeader: cfg.DefaultHeader, UserAgent: cfg.UserAgent, Debug: cfg.Debug, - Servers: []openapinlmflocationclient.ServerConfiguration{}, - OperationServers: make(map[string]openapinlmflocationclient.ServerConfigurations), + Servers: []nlmfocation.ServerConfiguration{}, + OperationServers: make(map[string]nlmfocation.ServerConfigurations), HTTPClient: cfg.HTTPClient, } for _, server := range cfg.Servers { - openapiServer := openapinlmflocationclient.ServerConfiguration{ + openapiServer := nlmfocation.ServerConfiguration{ URL: server.URL, Description: server.Description, - Variables: make(map[string]openapinlmflocationclient.ServerVariable), + Variables: make(map[string]nlmfocation.ServerVariable), } for name, variable := range server.Variables { - openapiServer.Variables[name] = openapinlmflocationclient.ServerVariable{ + openapiServer.Variables[name] = nlmfocation.ServerVariable{ Description: variable.Description, DefaultValue: variable.DefaultValue, EnumValues: variable.EnumValues, @@ -128,15 +162,15 @@ func NewLocationClient(cfg fivegc.ClientConfiguration) *LocationClient { openapiCfg.Servers = append(openapiCfg.Servers, openapiServer) } for name, servers := range cfg.OperationServers { - openapiServers := make(openapinlmflocationclient.ServerConfigurations, len(servers)) + openapiServers := make(nlmfocation.ServerConfigurations, len(servers)) for i, server := range servers { - openapiServers[i] = openapinlmflocationclient.ServerConfiguration{ + openapiServers[i] = nlmfocation.ServerConfiguration{ URL: server.URL, Description: server.Description, - Variables: make(map[string]openapinlmflocationclient.ServerVariable), + Variables: make(map[string]nlmfocation.ServerVariable), } for name, variable := range server.Variables { - openapiServers[i].Variables[name] = openapinlmflocationclient.ServerVariable{ + openapiServers[i].Variables[name] = nlmfocation.ServerVariable{ Description: variable.Description, DefaultValue: variable.DefaultValue, EnumValues: variable.EnumValues, @@ -146,36 +180,36 @@ func NewLocationClient(cfg fivegc.ClientConfiguration) *LocationClient { openapiCfg.OperationServers[name] = openapiServers } return &LocationClient{ - client: openapinlmflocationclient.NewAPIClient(openapiCfg), + client: nlmfocation.NewAPIClient(openapiCfg), } } // LocationContextTransfer returns location context transfer request -func (l LocationClient) LocationContextTransfer(ctx context.Context) openapinlmflocationclient.ApiLocationContextTransferRequest { +func (l LocationClient) LocationContextTransfer(ctx context.Context) nlmfocation.ApiLocationContextTransferRequest { return l.client.LocationContextTransferApi.LocationContextTransfer(ctx) } // LocationContextTransferExecute executes the location context transfer request -func (l LocationClient) LocationContextTransferExecute(r openapinlmflocationclient.ApiLocationContextTransferRequest) (*http.Response, error) { +func (l LocationClient) LocationContextTransferExecute(r nlmfocation.ApiLocationContextTransferRequest) (*http.Response, error) { return r.Execute() } // DetermineLocation returns determine location request -func (l LocationClient) DetermineLocation(ctx context.Context) openapinlmflocationclient.ApiDetermineLocationRequest { +func (l LocationClient) DetermineLocation(ctx context.Context) nlmfocation.ApiDetermineLocationRequest { return l.client.DetermineLocationApi.DetermineLocation(ctx) } // DetermineLocationExecute executes the determine location request -func (l LocationClient) DetermineLocationExecute(r openapinlmflocationclient.ApiDetermineLocationRequest) (*openapinlmflocationclient.LocationData, *http.Response, error) { +func (l LocationClient) DetermineLocationExecute(r nlmfocation.ApiDetermineLocationRequest) (*nlmfocation.LocationData, *http.Response, error) { return r.Execute() } // CancelLocation returns cancel location request -func (l LocationClient) CancelLocation(ctx context.Context) openapinlmflocationclient.ApiCancelLocationRequest { +func (l LocationClient) CancelLocation(ctx context.Context) nlmfocation.ApiCancelLocationRequest { return l.client.CancelLocationApi.CancelLocation(ctx) } // CancelLocationExecute executes the cancel location request -func (l LocationClient) CancelLocationExecute(r openapinlmflocationclient.ApiCancelLocationRequest) (*http.Response, error) { +func (l LocationClient) CancelLocationExecute(r nlmfocation.ApiCancelLocationRequest) (*http.Response, error) { return r.Execute() } diff --git a/fivegc/nlmf/mock/broadcast.go b/fivegc/nlmf/mock/broadcast.go index 1674573..897d4a3 100644 --- a/fivegc/nlmf/mock/broadcast.go +++ b/fivegc/nlmf/mock/broadcast.go @@ -1,60 +1,70 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ../broadcast.go + +// Package mock is a generated GoMock package. package mock import ( - "context" - "github.com/5GCoreNet/5GCoreNetSDK/fivegc" - openapinlmfbroadcast "github.com/5GCoreNet/server-openapi/Nlmf_Broadcast" + context "context" + reflect "reflect" + + fivegc "github.com/5GCoreNet/5GCoreNetSDK/fivegc" + nlmf "github.com/5GCoreNet/5GCoreNetSDK/fivegc/nlmf" + openapi_CommonData "github.com/5GCoreNet/openapi/openapi_CommonData" + openapi_Nlmf_Broadcast "github.com/5GCoreNet/openapi/openapi_Nlmf_Broadcast" + gomock "github.com/golang/mock/gomock" ) -// BroadcastMock is a mock of the Broadcast interface -type BroadcastMock struct { - cipherResponseData openapinlmfbroadcast.CipherResponseData - problemDetails openapinlmfbroadcast.ProblemDetails - redirectResponse fivegc.RedirectResponse - statusCode fivegc.StatusCode +// MockBroadcast is a mock of Broadcast interface. +type MockBroadcast struct { + ctrl *gomock.Controller + recorder *MockBroadcastMockRecorder } -// NewBroadcastMock creates a new mock of the Broadcast interface -func NewBroadcastMock( - cipherResponseData openapinlmfbroadcast.CipherResponseData, - problemDetails openapinlmfbroadcast.ProblemDetails, - redirectResponse fivegc.RedirectResponse, - statusCode fivegc.StatusCode, -) *BroadcastMock { - return &BroadcastMock{ - cipherResponseData: cipherResponseData, - problemDetails: problemDetails, - redirectResponse: redirectResponse, - statusCode: statusCode, - } +// MockBroadcastMockRecorder is the mock recorder for MockBroadcast. +type MockBroadcastMockRecorder struct { + mock *MockBroadcast } -// ProblemDetails allows to set the problem details of the mock -func (b *BroadcastMock) ProblemDetails(problemDetails openapinlmfbroadcast.ProblemDetails) { - b.problemDetails = problemDetails +// NewMockBroadcast creates a new mock instance. +func NewMockBroadcast(ctrl *gomock.Controller) *MockBroadcast { + mock := &MockBroadcast{ctrl: ctrl} + mock.recorder = &MockBroadcastMockRecorder{mock} + return mock } -// CipherResponseData allows to set the cipher response data of the mock -func (b *BroadcastMock) CipherResponseData(cipherResponseData openapinlmfbroadcast.CipherResponseData) { - b.cipherResponseData = cipherResponseData +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockBroadcast) EXPECT() *MockBroadcastMockRecorder { + return m.recorder } -// RedirectResponse allows to set the redirect response of the mock -func (b *BroadcastMock) RedirectResponse(redirectResponse fivegc.RedirectResponse) { - b.redirectResponse = redirectResponse +// CipherKeyData mocks base method. +func (m *MockBroadcast) CipherKeyData(arg0 context.Context, arg1 openapi_Nlmf_Broadcast.CipherRequestData) (openapi_Nlmf_Broadcast.CipherResponseData, openapi_CommonData.ProblemDetails, fivegc.RedirectResponse, nlmf.CypherResponseStatusCode) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CipherKeyData", arg0, arg1) + ret0, _ := ret[0].(openapi_Nlmf_Broadcast.CipherResponseData) + ret1, _ := ret[1].(openapi_CommonData.ProblemDetails) + ret2, _ := ret[2].(fivegc.RedirectResponse) + ret3, _ := ret[3].(nlmf.CypherResponseStatusCode) + return ret0, ret1, ret2, ret3 } -// StatusCode allows to set the status code of the mock -func (b *BroadcastMock) StatusCode(statusCode fivegc.StatusCode) { - b.statusCode = statusCode +// CipherKeyData indicates an expected call of CipherKeyData. +func (mr *MockBroadcastMockRecorder) CipherKeyData(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CipherKeyData", reflect.TypeOf((*MockBroadcast)(nil).CipherKeyData), arg0, arg1) } -// Error returns the problem details of the mock -func (b *BroadcastMock) Error(ctx context.Context, err error) openapinlmfbroadcast.ProblemDetails { - return b.problemDetails +// Error mocks base method. +func (m *MockBroadcast) Error(ctx context.Context, err error) openapi_CommonData.ProblemDetails { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Error", ctx, err) + ret0, _ := ret[0].(openapi_CommonData.ProblemDetails) + return ret0 } -// CipherKeyData returns the cipher response data, the problem details, the redirect response and the status code of the mock -func (b *BroadcastMock) CipherKeyData(ctx context.Context, data openapinlmfbroadcast.CipherRequestData) (openapinlmfbroadcast.CipherResponseData, openapinlmfbroadcast.ProblemDetails, fivegc.RedirectResponse, fivegc.StatusCode) { - return b.cipherResponseData, b.problemDetails, b.redirectResponse, b.statusCode +// Error indicates an expected call of Error. +func (mr *MockBroadcastMockRecorder) Error(ctx, err interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Error", reflect.TypeOf((*MockBroadcast)(nil).Error), ctx, err) } diff --git a/fivegc/nlmf/mock/generate.go b/fivegc/nlmf/mock/generate.go new file mode 100644 index 0000000..7550837 --- /dev/null +++ b/fivegc/nlmf/mock/generate.go @@ -0,0 +1,4 @@ +package mock + +//go:generate mockgen -source=../broadcast.go -destination=broadcast.go -package=mock +//go:generate mockgen -source=../location.go -destination=location.go -package=mock diff --git a/fivegc/nlmf/mock/location.go b/fivegc/nlmf/mock/location.go index 39adaa6..a8b0104 100644 --- a/fivegc/nlmf/mock/location.go +++ b/fivegc/nlmf/mock/location.go @@ -1,70 +1,102 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ../location.go + +// Package mock is a generated GoMock package. package mock import ( - "context" - "github.com/5GCoreNet/5GCoreNetSDK/fivegc" - openapinlmflocation "github.com/5GCoreNet/server-openapi/Nlmf_Location" + context "context" + reflect "reflect" + + fivegc "github.com/5GCoreNet/5GCoreNetSDK/fivegc" + nlmf "github.com/5GCoreNet/5GCoreNetSDK/fivegc/nlmf" + openapi_CommonData "github.com/5GCoreNet/openapi/openapi_CommonData" + openapi_Nlmf_Location "github.com/5GCoreNet/openapi/openapi_Nlmf_Location" + gomock "github.com/golang/mock/gomock" ) -// LocationMock is a mock of the Location interface -type LocationMock struct { - locationData openapinlmflocation.LocationData - problemDetails openapinlmflocation.ProblemDetails - redirectResponse fivegc.RedirectResponse - statusCode fivegc.StatusCode +// MockLocation is a mock of Location interface. +type MockLocation struct { + ctrl *gomock.Controller + recorder *MockLocationMockRecorder +} + +// MockLocationMockRecorder is the mock recorder for MockLocation. +type MockLocationMockRecorder struct { + mock *MockLocation +} + +// NewMockLocation creates a new mock instance. +func NewMockLocation(ctrl *gomock.Controller) *MockLocation { + mock := &MockLocation{ctrl: ctrl} + mock.recorder = &MockLocationMockRecorder{mock} + return mock } -// NewLocationMock creates a new mock of the Location interface -func NewLocationMock( - locationData openapinlmflocation.LocationData, - problemDetails openapinlmflocation.ProblemDetails, - redirectResponse fivegc.RedirectResponse, - statusCode fivegc.StatusCode, -) *LocationMock { - return &LocationMock{ - locationData: locationData, - problemDetails: problemDetails, - redirectResponse: redirectResponse, - statusCode: statusCode, - } +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockLocation) EXPECT() *MockLocationMockRecorder { + return m.recorder } -// ProblemDetails allows to set the problem details of the mock -func (l *LocationMock) ProblemDetails(problemDetails openapinlmflocation.ProblemDetails) { - l.problemDetails = problemDetails +// CancelLocation mocks base method. +func (m *MockLocation) CancelLocation(arg0 context.Context, arg1 openapi_Nlmf_Location.CancelLocData) (openapi_CommonData.ProblemDetails, fivegc.RedirectResponse, nlmf.CancelLocationStatusCode) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CancelLocation", arg0, arg1) + ret0, _ := ret[0].(openapi_CommonData.ProblemDetails) + ret1, _ := ret[1].(fivegc.RedirectResponse) + ret2, _ := ret[2].(nlmf.CancelLocationStatusCode) + return ret0, ret1, ret2 } -// LocationData allows to set the location data of the mock -func (l *LocationMock) LocationData(locationData openapinlmflocation.LocationData) { - l.locationData = locationData +// CancelLocation indicates an expected call of CancelLocation. +func (mr *MockLocationMockRecorder) CancelLocation(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CancelLocation", reflect.TypeOf((*MockLocation)(nil).CancelLocation), arg0, arg1) } -// RedirectResponse allows to set the redirect response of the mock -func (l *LocationMock) RedirectResponse(redirectResponse fivegc.RedirectResponse) { - l.redirectResponse = redirectResponse +// DetermineLocation mocks base method. +func (m *MockLocation) DetermineLocation(arg0 context.Context, arg1 openapi_Nlmf_Location.InputData) (openapi_Nlmf_Location.LocationData, openapi_CommonData.ProblemDetails, fivegc.RedirectResponse, nlmf.DetermineLocationStatusCode) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DetermineLocation", arg0, arg1) + ret0, _ := ret[0].(openapi_Nlmf_Location.LocationData) + ret1, _ := ret[1].(openapi_CommonData.ProblemDetails) + ret2, _ := ret[2].(fivegc.RedirectResponse) + ret3, _ := ret[3].(nlmf.DetermineLocationStatusCode) + return ret0, ret1, ret2, ret3 } -// StatusCode allows to set the status code of the mock -func (l *LocationMock) StatusCode(statusCode fivegc.StatusCode) { - l.statusCode = statusCode +// DetermineLocation indicates an expected call of DetermineLocation. +func (mr *MockLocationMockRecorder) DetermineLocation(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DetermineLocation", reflect.TypeOf((*MockLocation)(nil).DetermineLocation), arg0, arg1) } -// Error returns the problem details of the mock -func (l *LocationMock) Error(ctx context.Context, err error) openapinlmflocation.ProblemDetails { - return l.problemDetails +// Error mocks base method. +func (m *MockLocation) Error(ctx context.Context, err error) openapi_CommonData.ProblemDetails { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Error", ctx, err) + ret0, _ := ret[0].(openapi_CommonData.ProblemDetails) + return ret0 } -// CancelLocation returns the problem details, the redirect response and the status code of the mock -func (l *LocationMock) CancelLocation(ctx context.Context, data openapinlmflocation.CancelLocData) (openapinlmflocation.ProblemDetails, fivegc.RedirectResponse, fivegc.StatusCode) { - return l.problemDetails, l.redirectResponse, l.statusCode +// Error indicates an expected call of Error. +func (mr *MockLocationMockRecorder) Error(ctx, err interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Error", reflect.TypeOf((*MockLocation)(nil).Error), ctx, err) } -// DetermineLocation returns the location data, the problem details, the redirect response and the status code of the mock -func (l *LocationMock) DetermineLocation(ctx context.Context, data openapinlmflocation.InputData) (openapinlmflocation.LocationData, openapinlmflocation.ProblemDetails, fivegc.RedirectResponse, fivegc.StatusCode) { - return l.locationData, l.problemDetails, l.redirectResponse, l.statusCode +// LocationContextTransfer mocks base method. +func (m *MockLocation) LocationContextTransfer(arg0 context.Context, arg1 openapi_Nlmf_Location.LocContextData) (openapi_CommonData.ProblemDetails, fivegc.RedirectResponse, nlmf.LocationContextTransferStatusCode) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "LocationContextTransfer", arg0, arg1) + ret0, _ := ret[0].(openapi_CommonData.ProblemDetails) + ret1, _ := ret[1].(fivegc.RedirectResponse) + ret2, _ := ret[2].(nlmf.LocationContextTransferStatusCode) + return ret0, ret1, ret2 } -// LocationContextTransfer returns the problem details, the redirect response and the status code of the mock -func (l *LocationMock) LocationContextTransfer(ctx context.Context, data openapinlmflocation.LocContextData) (openapinlmflocation.ProblemDetails, fivegc.RedirectResponse, fivegc.StatusCode) { - return l.problemDetails, l.redirectResponse, l.statusCode +// LocationContextTransfer indicates an expected call of LocationContextTransfer. +func (mr *MockLocationMockRecorder) LocationContextTransfer(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LocationContextTransfer", reflect.TypeOf((*MockLocation)(nil).LocationContextTransfer), arg0, arg1) } diff --git a/fivegc/nlmf/nlmf.go b/fivegc/nlmf/server.go similarity index 73% rename from fivegc/nlmf/nlmf.go rename to fivegc/nlmf/server.go index 4d30e37..8efc843 100644 --- a/fivegc/nlmf/nlmf.go +++ b/fivegc/nlmf/server.go @@ -1,15 +1,13 @@ package nlmf import ( - "fmt" "github.com/gin-gonic/gin" "log" ) // Server represents a NLMF server. type Server struct { - ip string - port string + address string // IP:PORT apiRoot string location Location broadcast Broadcast @@ -19,22 +17,22 @@ type Server struct { } // NewServer creates a new Server NLMF server instance. -func NewServer(ip string, port string, apiRoot string, logger *log.Logger) *Server { +// The address is the IP:PORT of the NLMF server. +func NewServer(address string, apiRoot string, logger *log.Logger) *Server { return &Server{ - ip: ip, - port: port, + address: address, apiRoot: apiRoot, logger: logger, stop: make(chan bool), } } -// AttachLocation attaches a Location client to the NLMF Server. +// AttachLocation attaches a Location handler to the NLMF Server. func (n *Server) AttachLocation(l Location) { n.location = l } -// AttachBroadcast attaches a Broadcast client to the NLMF Server. +// AttachBroadcast attaches a Broadcast handler to the NLMF Server. func (n *Server) AttachBroadcast(b Broadcast) { n.broadcast = b } @@ -51,7 +49,7 @@ func (n *Server) Start() { if n.broadcast != nil { attachBroadcastHandler(root, n.broadcast) } - go n.router.Run(fmt.Sprintf("%s:%s", n.ip, n.port)) + go n.router.Run(n.address) <-n.stop return } diff --git a/fivegc/redirect.go b/fivegc/redirect.go index c1cb29e..f779734 100644 --- a/fivegc/redirect.go +++ b/fivegc/redirect.go @@ -12,9 +12,9 @@ type RedirectResponse struct { Cause string `json:"cause,omitempty"` - // String providing an URI formatted according to RFC 3986. + // String providing a URI formatted according to RFC 3986. TargetScp string `json:"targetScp,omitempty"` - // String providing an URI formatted according to RFC 3986. + // String providing a URI formatted according to RFC 3986. TargetSepp string `json:"targetSepp,omitempty"` } diff --git a/fivegc/server.go b/fivegc/server.go new file mode 100644 index 0000000..73bcf68 --- /dev/null +++ b/fivegc/server.go @@ -0,0 +1,11 @@ +package fivegc + +import ( + "context" + openapicommon "github.com/5GCoreNet/openapi/openapi_CommonData" +) + +type CommonInterface interface { + // Error returns a problem details, it is used to handle errors when unmarshalling the request. + Error(ctx context.Context, err error) openapicommon.ProblemDetails +} diff --git a/fivegc/status.go b/fivegc/status.go index 706e796..419afa5 100644 --- a/fivegc/status.go +++ b/fivegc/status.go @@ -1,5 +1,7 @@ package fivegc +// Note that this file is a port of the file status.go from Go standard library. + // StatusCode represents the status code of the HTTP response. type StatusCode uint16 diff --git a/go.mod b/go.mod index 9bcd103..1a55bd3 100644 --- a/go.mod +++ b/go.mod @@ -1,11 +1,11 @@ module github.com/5GCoreNet/5GCoreNetSDK -go 1.18 +go 1.20 require ( - github.com/5GCoreNet/client-openapi v0.0.0-20230128134546-a505b74025b3 - github.com/5GCoreNet/server-openapi v0.0.0-20230126185629-9202d02e7eea + github.com/5GCoreNet/openapi v1.18.2 github.com/gin-gonic/gin v1.8.2 + github.com/golang/mock v1.6.0 ) require ( @@ -23,10 +23,10 @@ require ( github.com/pelletier/go-toml/v2 v2.0.6 // indirect github.com/ugorji/go/codec v1.2.7 // indirect golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 // indirect - golang.org/x/net v0.5.0 // indirect - golang.org/x/oauth2 v0.4.0 // indirect - golang.org/x/sys v0.4.0 // indirect - golang.org/x/text v0.6.0 // indirect + golang.org/x/net v0.9.0 // indirect + golang.org/x/oauth2 v0.7.0 // indirect + golang.org/x/sys v0.7.0 // indirect + golang.org/x/text v0.9.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..584c2f5 --- /dev/null +++ b/go.sum @@ -0,0 +1,128 @@ +github.com/5GCoreNet/openapi v1.18.2 h1:4HRDOTapVY+Xr49KmiS7R4TAuwMEpNwg6Z+oiXM0mnY= +github.com/5GCoreNet/openapi v1.18.2/go.mod h1:10RqDFgIY+AgfRk626E0+3e9NbooVSvE/SsZs4mQa1w= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.8.2 h1:UzKToD9/PoFj/V4rvlKqTRKnQYyz8Sc1MJlv4JHPtvY= +github.com/gin-gonic/gin v1.8.2/go.mod h1:qw5AYuDrzRTnhvusDsrov+fDIxp9Dleuu12h8nfB398= +github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= +github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= +github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk= +github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= +github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= +github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= +github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M= +golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g= +golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/header/header_test.go b/internal/header/header_test.go new file mode 100644 index 0000000..b07bbd9 --- /dev/null +++ b/internal/header/header_test.go @@ -0,0 +1,24 @@ +package header + +import ( + "github.com/5GCoreNet/5GCoreNetSDK/fivegc" + "github.com/gin-gonic/gin" + "net/http/httptest" + "testing" +) + +func TestBindRedirectHeader(t *testing.T) { + ginContext, _ := gin.CreateTestContext(&httptest.ResponseRecorder{}) + BindRedirectHeader(ginContext, fivegc.RedirectHeader{ + Location: "http://localhost:8080", + SbiTarget: "1234", + }) + + if ginContext.Writer.Header().Get("Location") != "http://localhost:8080" { + t.Errorf("Location header not set") + } + + if ginContext.Writer.Header().Get("3gpp-Sbi-Target-Nf-Id") != "1234" { + t.Errorf("3gpp-Sbi-Target-Nf-Id header not set") + } +}