docs: add detailed usage documentation together with examples (#29)

* feat: rename Pass to Password in ClientConfig

* docs: add more detailed usage documentation together with examples

* chore: update issue templates

* docs: add codeowners, code of conduct and contributing docs

* chore: add editorconfig

* chore: apply linter
This commit is contained in:
Mateusz Filipowicz
2025-02-18 22:36:08 +01:00
committed by GitHub
parent 5b32a4763e
commit 35e7b2c3cc
16 changed files with 1177 additions and 49 deletions

67
.editorconfig Normal file
View File

@@ -0,0 +1,67 @@
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = false
max_line_length = 200
tab_width = 4
# this affects things like 'throws' in method declaration in the new line etc
ij_continuation_indent_size = 8
ij_formatter_off_tag = @formatter:off
ij_formatter_on_tag = @formatter:on
ij_formatter_tags_enabled = false
ij_smart_tabs = false
ij_wrap_on_typing = false
ij_any_block_comment_add_space = true
[*.conf]
ij_continuation_indent_size = 2
[*.properties]
ij_properties_align_group_field_declarations = false
ij_properties_keep_blank_lines = false
ij_properties_key_value_delimiter = equals
ij_properties_spaces_around_key_value_delimiter = false
[.editorconfig]
max_line_length = 300
ij_editorconfig_align_group_field_declarations = false
ij_editorconfig_space_after_colon = false
ij_editorconfig_space_after_comma = true
ij_editorconfig_space_before_colon = false
ij_editorconfig_space_before_comma = false
ij_editorconfig_spaces_around_assignment_operators = true
[BUCK]
indent_size = 4
tab_width = 4
ij_buck_keep_indents_on_empty_lines = false
[{*.bash,*.sh,*.zsh}]
ij_shell_binary_ops_start_line = false
ij_shell_keep_column_alignment_padding = false
ij_shell_minify_program = false
ij_shell_redirect_followed_by_space = false
ij_shell_switch_cases_indented = false
[{*.har,*.json}]
ij_json_keep_blank_lines_in_code = 0
ij_json_keep_indents_on_empty_lines = false
ij_json_keep_line_breaks = true
ij_json_space_after_colon = true
ij_json_space_after_comma = true
ij_json_space_before_colon = true
ij_json_space_before_comma = false
ij_json_spaces_within_braces = false
ij_json_spaces_within_brackets = false
ij_json_wrap_long_lines = false
[{*.yaml,*.yml}]
indent_size = 2
tab_width = 2
ij_yaml_keep_indents_on_empty_lines = false
ij_yaml_keep_line_breaks = true
ij_yaml_space_before_colon = false
ij_yaml_spaces_within_braces = true
ij_yaml_spaces_within_brackets = true

1
.github/CODEOWNERS vendored Normal file
View File

@@ -0,0 +1 @@
* @filipowm

132
.github/CODE_OF_CONDUCT.md vendored Normal file
View File

@@ -0,0 +1,132 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall
community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or advances of
any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address,
without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders 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, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official email address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
[INSERT CONTACT METHOD].
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of
actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the
community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.1, available at
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
[https://www.contributor-covenant.org/translations][translations].
[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations

199
.github/CONTRIBUTING.md vendored Normal file
View File

@@ -0,0 +1,199 @@
<!-- omit in toc -->
# Contributing to go-unifi
First off, thanks for taking the time to contribute! ❤️
All types of contributions are encouraged and valued. See the [Table of Contents](#table-of-contents) for different ways to help and details about how this project handles them. Please make sure to
read the relevant section before making your contribution. It will make it a lot easier for us maintainers and smooth out the experience for all involved. The community looks forward to your
contributions. 🎉
> And if you like the project, but just don't have time to contribute, that's fine. There are other easy ways to support the project and show your appreciation, which we would also be very happy
> about:
> - Star the project
> - Tweet about it and share on different social media
> - Refer this project in your project's readme
> - Mention the project at local meetups and tell your friends/colleagues
<!-- omit in toc -->
## Table of Contents
- [I Have a Question](#i-have-a-question)
- [I Want To Contribute](#i-want-to-contribute)
- [Reporting Bugs](#reporting-bugs)
- [Suggesting Enhancements](#suggesting-enhancements)
- [Your First Code Contribution](#your-first-code-contribution)
- [Improving The Documentation](#improving-the-documentation)
- [Styleguides](#styleguides)
- [Commit Messages](#commit-messages)
- [Join The Project Team](#join-the-project-team)
## I Have a Question
> If you want to ask a question, we assume that you have read the available [Documentation](https://github.com/filipowm/go-unifi/blob/main/docs/readme.go).
Before you ask a question, it is best to search for existing [Discussions](https://github.com/filipowm/go-unifi/discussions) or [issues](https://github.com/filipowm/go-unifi/issues)
that might help you. In case you have found a suitable issue and still need clarification, you can write your question in this issue. It is also advisable to search the internet for answers first.
If you then still feel the need to ask a question and need clarification, we recommend the following:
- Open a [Discussion](https://github.com/filipowm/go-unifi/discussions/new/choose).
- Provide as much context as you can about what you're running into.
- Provide project and platform versions depending on what seems relevant.
We will then take care of the question as soon as possible.
## I Want To Contribute
### Reporting Bugs
<!-- omit in toc -->
#### Before Submitting a Bug Report
A good bug report shouldn't leave others needing to chase you up for more information. Therefore, we ask you to investigate carefully, collect information and describe the issue in detail in your
report. Please complete the following steps in advance to help us fix any potential bug as fast as possible.
- Make sure that you are using the latest version, especially importat in case of minor/patch versions that may contain fixes of your issue.
- Determine if your bug is really a bug and not an error on your side e.g. using incompatible environment components/versions (Make sure that you have read
the [documentation](https://github.com/filipowm/go-unifi/blob/main/docs/readme.go). If you are looking for support, you might want to check [this section](#i-have-a-question)).
- To see if other users have experienced (and potentially already solved) the same issue you are having, check if there is not already a bug report existing for your bug or error in
the [bug tracker](https://github.com/filipowm/go-unifi/issues?q=label%3Abug).
- Also make sure to search the internet (including Stack Overflow, Reddit) to see if users outside of the GitHub community have discussed the issue.
- Collect information about the bug:
- Stack trace (Traceback), logs
- OS, Platform and Version (Windows, Linux, macOS, x86, ARM)
- Version of the interpreter, compiler, SDK, runtime environment, package manager, depending on what seems relevant.
- Possibly your input and the output
- Can you reliably reproduce the issue? And can you also reproduce it with older versions?
<!-- omit in toc -->
#### How Do I Submit a Good Bug Report?
> You must never report security related issues, vulnerabilities or bugs including sensitive information to the issue tracker, or elsewhere in public. Instead sensitive bugs must be sent by email
> to <>.
<!-- You may add a PGP key to allow the messages to be sent encrypted as well. -->
We use GitHub issues to track bugs and errors. If you run into an issue with the project:
- Open an [Issue](https://github.com/filipowm/go-unifi/issues/new?template=bug_report.md)
- Explain the behavior you would expect and the actual behavior.
- Please provide as much context as possible and describe the *reproduction steps* that someone else can follow to recreate the issue on their own. This usually includes your code. For good bug
reports you should isolate the problem and create a reduced test case.
- Provide the information you collected in the previous section.
Once it's filed:
- The project team will label the issue accordingly.
- A team member will try to reproduce the issue with your provided steps. If there are no reproduction steps or no obvious way to reproduce the issue, the team will ask you for those steps and mark
the issue as `needs-repro`. Bugs with the `needs-repro` tag will not be addressed until they are reproduced.
- If the team is able to reproduce the issue, it will be marked `needs-fix`, as well as possibly other tags (such as `critical`), and the issue will be left to
be [implemented by someone](#your-first-code-contribution).
<!-- You might want to create an issue template for bugs and errors that can be used as a guide and that defines the structure of the information to be included. If you do so, reference it here in the description. -->
### Suggesting Enhancements
This section guides you through submitting an enhancement suggestion for go-unifi, **including completely new features and minor improvements to existing functionality**. Following these guidelines
will help maintainers and the community to understand your suggestion and find related suggestions.
<!-- omit in toc -->
#### Before Submitting an Enhancement
- Make sure that you are using the latest version.
- Read the [documentation](https://github.com/filipowm/go-unifi/blob/main/docs/readme.go) carefully and find out if the functionality is already covered, maybe by an individual configuration.
- Perform a [search](https://github.com/filipowm/go-unifi/issues) to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one.
- Find out whether your idea fits with the scope and aims of the project. It's up to you to make a strong case to convince the project's developers of the merits of this feature. Keep in mind that we
want features that will be useful to the majority of our users and not just a small subset. If you're just targeting a minority of users, consider writing an add-on/plugin library.
<!-- omit in toc -->
#### How Do I Submit a Good Enhancement Suggestion?
Enhancement suggestions are tracked as [GitHub issues](https://github.com/filipowm/go-unifi/issues).
- Use a **clear and descriptive title** for the issue to identify the suggestion.
- Provide a **step-by-step description of the suggested enhancement** in as many details as possible.
- **Describe the current behavior** and **explain which behavior you expected to see instead** and why. At this point you can also tell which alternatives do not work for you.
- You may want to **include screenshots or screen recordings** which help you demonstrate the steps or point out the part which the suggestion is related to. You can
use [LICEcap](https://www.cockos.com/licecap/) to record GIFs on macOS and Windows, and the
built-in [screen recorder in GNOME](https://help.gnome.org/users/gnome-help/stable/screen-shot-record.html.en) or [SimpleScreenRecorder](https://github.com/MaartenBaert/ssr) on
Linux. <!-- this should only be included if the project has a GUI -->
- **Explain why this enhancement would be useful** to most go-unifi users. You may also want to point out the other projects that solved it better and which could serve as inspiration.
<!-- You might want to create an issue template for enhancement suggestions that can be used as a guide and that defines the structure of the information to be included. If you do so, reference it here in the description. -->
### Your First Code Contribution
1. Clone the repository
```bash
git clone https://github.com/filipowm/go-unifi.git
```
2. Ensure you have the Go version installed as defined in `go.mod` file
```bash
go version
```
If you don't have the correct version, you can download it from the [official Go website](https://golang.org/dl/).
If you have multiple versions of Go installed, you can use a tool like [gvm](https://github.com/moovweb/gvm)
3. Ensure you have the latest version of the project
```bash
git pull
```
4. Create a new branch
```bash
git checkout -b my-new-feature
```
5. Make your changes
6. Run the tests
```bash
go test ./...
```
7. Lint your code using [golangci-lint](https://golangci-lint.run/)
```bash
golangci-lint run --fix
```
8. If tests are passing and code is linted, you can create a commit and then pull request following styleguides below.
### Improving The Documentation
Any contribution to the documentation is highly appreciated. This includes both improvements to the existing documentation and adding new content. That's the most lightweight way to start
contributing to the project.
## Styleguides
### Commit Messages
Please follow [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) standard for commit messages whenever possible. That's the best way to ensure that your commits are easy to
understand and follow the project's versioning. If you are not familiar with the conventional commits, you can also use the following format:
```
<type>: <subject>
```
The `<type>` must be one of the following:
- `feat`: A new feature
- `fix`: A bug fix
- `docs`: Documentation only changes
- `chore`: Changes to the build process, auxiliary tools, documentation, linting etc
- `refactor`: A code change that neither fixes a bug nor adds a feature
The `<subject>` should be a short description of the change. Use the imperative, present tense.
### Pull Requests
Please follow conventional commits
## Join The Project Team
Create a discussion thread that you would like to join as a team member. New team members are always welcome. We are looking for people who are willing to contribute and help the project grow.

View File

@@ -1,17 +1,78 @@
---
name: Bug report
name: 🐞Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
title: '[BUG] '
labels: "bug"
assignees: 'filipowm'
---
## Expected Behavior
## Actual Behavior
## Steps to Reproduce the Problem
1.
1.
1.
## Specifications
- Version:
- Platform:
- Subsystem:
# **🐞 Bug Report**
## **Describe the bug**
<!-- A clear and concise description of what the bug is. -->
*
---
### **Is this a regression?**
<!-- Did this behaviour used to work in the previous version? -->
<!-- Yes, the last version in which this bug was not present was: ... -->
---
### **To Reproduce**
<!-- Steps to reproduce the error:
(e.g.:)
1. Use x argument / navigate to
2. Fill this information
3. Go to...
4. See error -->
<!-- Write the steps here (add or remove as many steps as needed)-->
1.
2.
3.
4.
---
### **Expected behaviour**
<!-- A clear and concise description of what you expected to happen. -->
*
---
### **Media prove**
<!-- If applicable, add logs, screenshots or videos to help explain your problem. -->
---
### **Your environment**
<!-- use all the applicable bulleted list elements for this specific issue,
and remove all the bulleted list elements that are not relevant for this issue. -->
* OS: <!--[e.g. Ubuntu 5.4.0-26-generic x86_64 / Windows 1904 ...]-->
* Node version:
* Npm version:
* Browser name and version:
---
### **Additional context**
<!-- Add any other context or additional information about the problem here.-->
*
<!--📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛
Oh, hi there! 😄
To expedite issue processing, please search open and closed issues before submitting a new one.
Please read our Rules of Conduct at this repository's `.github/CODE_OF_CONDUCT.md`
📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛-->

View File

@@ -1 +1,2 @@
---
blank_issues_enabled: false

View File

@@ -1,23 +1,45 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: feature
assignees: ''
name: "🚀 Feature Request"
about: "Suggest an idea or possible new feature for this project."
title: ""
labels: "feature"
assignees: "filipowm"
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
# **🚀 Feature Request**
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
## **Is your feature request related to a problem? Please describe.**
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
*
**Additional context**
Add any other context or screenshots about the feature request here.
---
**Would you like to contribute?**
[ ] YES!
## **Describe the solution you'd like**
<!-- A clear and concise description of what you want to happen. -->
*
---
## **Describe alternatives you've considered**
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
*
---
### **Additional context**
<!-- Add any other context or additional information about the problem here.-->
*
<!--📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛
Oh, hi there! 😄
To expedite issue processing, please search open and closed issues before submitting a new one.
Please read our Rules of Conduct at this repository's `.github/CODE_OF_CONDUCT.md`
📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛-->

View File

@@ -1,4 +1,5 @@
# UniFi Go SDK
[![Docs](https://img.shields.io/badge/docs-reference-blue)](https://pkg.go.dev/github.com/filipowm/go-unifi)
[![GoDoc](https://godoc.org/github.com/filipowm/go-unifi?status.svg)](https://godoc.org/github.com/filipowm/go-unifi)
![GitHub Release](https://img.shields.io/github/v/release/filipowm/go-unifi)
![GitHub branch check runs](https://img.shields.io/github/check-runs/filipowm/go-unifi/main)
@@ -10,12 +11,18 @@ but can be used independently for any Go project requiring UniFi Network Control
## Features
- Great UniFi Network Controller API coverage through automated code generation and manually added code for undocumented endpoints
- Easy to use client with support for API Key and username/password authentication
- Generated data models from UniFi Controller API specifications
- Daily automated updates to track the latest UniFi Controller versions
- Easy to use client with support for API Key and username/password authentication
- Support for multiple UniFi Controller versions
- Strong typing for all API models with Go structs
## Supported UniFi Controller Versions
Any version after 5.12.35 is supported as of now. **Latest version: 9.0.114**.
The SDK is updated daily to track the latest UniFi Controller versions.
If you encounter any issues with the latest UniFi Controller version, please open an issue.
## Code Generation
The data models and basic REST methods are generated from JSON specifications found in the UniFi Controller JAR files. Those JSON specs show all fields and the associated regex/validation information.
@@ -176,15 +183,15 @@ user, err := c.CreateUser(ctx, "site-name", &unifi.User{
## Plans
- [ ] Increase API coverage, or modify code generation to rely on the official UniFi Controller API specifications
- [x] Increase API coverage, or modify code generation to rely on the official UniFi Controller API specifications
- [x] Improve error handling (currently only basic error handling is implemented and error details are not propagated)
- [x] Improve client code for better usability
- [x] Support API Key authentication
- [ ] Generate client code for currently generated API structures, for use within or outside the Terraform provider
- [x] Generate client code for currently generated API structures, for use within or outside the Terraform provider
- [ ] Increase test coverage
- [x] Implement validation for fields and structures
- [ ] Extend validators for more complex cases
- [ ] Add more documentation and examples
- [x] Add more documentation and examples
- [ ] Bugfixing...
## Contributing

169
docs/advanced_topics.md Normal file
View File

@@ -0,0 +1,169 @@
# Advanced Topics
This document delves into advanced aspects of using the UniFi Go SDK client, explaining how to customize the HTTP client,
use interceptors effectively, handle errors robustly, and extend validations.
## Making a raw API Call using SDK Methods
For endpoints that are not directly covered by a specialized client method, the UniFi Go SDK provides a set of helper methods for making requests to UniFi API. These methods simplify API interactions
by handling common tasks such as request construction, JSON marshaling of the request body, authentication, applying interceptors, error handling, and decoding the response:
- **Do**: The core method that performs an HTTP request with a given method, API path, request body, and destination for decoding the response. It handles validation, URL construction, interceptors,
and error processing.
- **Get**: A convenience wrapper around **Do** that executes an HTTP GET request.
- **Post**: A convenience wrapper to perform an HTTP POST request.
- **Put**: Similar to Post, but for HTTP PUT requests.
- **Delete**: Performs an HTTP DELETE request.
These methods are used internally by higher level functions, such as those in `unifi/device.generated.go` and `unifi/device.go`. For example, when creating a new device, the SDK calls `Post` to send
the device data to the UniFi Controller API, while `Get` is used to retrieve device information.
Here is an example of using these methods for a custom API operation:
```go
// Define a custom response structure
var respData struct {
Meta Meta `json:"meta"`
Data interface{} `json:"data"`
}
// Use the Get method to fetch data from a custom endpoint
err := c.Get(ctx, "/api/customEndpoint", nil, &respData)
if err != nil {
log.Fatalf("Error performing GET request: %v", err)
}
// For a POST request, define your request payload and response structure:
reqPayload := struct {
Field1 string `json:"field1"`
Field2 int `json:"field2"`
}{
Field1: "value",
Field2: 123,
}
var postResp struct {
Meta Meta `json:"meta"`
Data interface{} `json:"data"`
}
err = c.Post(ctx, "/api/customPostEndpoint", reqPayload, &postResp)
if err != nil {
log.Fatalf("Error performing POST request: %v", err)
}
// do something with the response
```
These helper methods abstract away the boilerplate of manually constructing HTTP requests and processing responses, allowing you to focus on your application's logic while leveraging built-in
validation and error handling provided by the SDK.
## Customizing the HTTP Client
While the basic configuration allows simple modifications, you can fully customize the underlying HTTP client for more
control over connection settings, proxy configuration, TLS settings, and connection pooling.
Example:
```go
c, err := unifi.NewClient(&unifi.ClientConfig{
BaseURL: "https://unifi.localdomain",
APIKey: "your-api-key",
HttpCustomizer: func (transport *http.Transport) error {
transport.MaxIdleConns = 20
transport.IdleConnTimeout = 90 * time.Second
// Customize TLS settings or add a proxy configuration
return nil
},
})
if err != nil {
log.Fatalf("Error creating client: %v", err)
}
```
## Interceptors and Middleware
Interceptors provide hooks into the request/response cycle and can be used for logging, metrics collection, or modifying
requests before they are sent. They implement the [ClientInterceptor](https://pkg.go.dev/github.com/filipowm/go-unifi/unifi#ClientInterceptor) interface.
### Example: Advanced Logging Interceptor
```go
// AdvancedLoggingInterceptor logs HTTP details and measures request time
type AdvancedLoggingInterceptor struct {}
func (a *AdvancedLoggingInterceptor) InterceptRequest(req *http.Request) error {
log.Printf("[Request] %s %s", req.Method, req.URL)
req = req.WithContext(context.WithValue(req.Context(), "start", time.Now()))
return nil
}
func (a *AdvancedLoggingInterceptor) InterceptResponse(resp *http.Response) error {
if start, ok := resp.Request.Context().Value("start").(time.Time); ok {
duration := time.Since(start)
log.Printf("[Response] %s %s in %v", resp.Request.Method, resp.Request.URL, duration)
}
return nil
}
c, err := unifi.NewClient(&unifi.ClientConfig{
BaseURL: "https://unifi.localdomain",
APIKey: "your-api-key",
Interceptors: []unifi.ClientInterceptor{&AdvancedLoggingInterceptor{}},
})
if err != nil {
log.Fatalf("Error creating client: %v", err)
}
```
## Debugging and Logging
For troubleshooting, it might be useful to enable verbose logging. You can implement an interceptor to log additional
details like headers, body content, and timings. This can be enabled conditionally in your application's debug mode.
## Advanced Error Handling
The client supports both soft and hard validation modes. When using hard validation, errors returned are of type
`unifi.ValidationError` containing details about which fields failed validation.
Example error handling snippet:
```go
n := &unifi.Network{
Name: "my-network",
Purpose: "invalid-purpose",
IPSubnet: "10.0.0.10/24",
}
_, err = c.CreateNetwork(ctx, "default", n)
if err != nil {
var validationErr *unifi.ValidationError
if errors.As(err, &validationErr) {
// Process detailed validation errors
for field, errMsg := range validationErr.Root {
log.Printf("Validation error on %s: %s", field, errMsg)
}
} else {
log.Fatalf("Error creating network: %v", err)
}
}
```
## Extending Validations
If the default validations do not meet your needs, you can implement custom validation logic. Extend the SDK's validation rules by wrapping or augmenting the existing ones. For example,
you can create a custom validator function and integrate it into your client initialization. Check [validation.go](../unifi/validation.go) for details.
## Contributing and Extending the SDK
The UniFi Go SDK is designed to be adaptable:
- **Feature Requests:** If the SDK does not support a particular API endpoint, consider contributing by opening an issue or a pull request.
- **Custom Extensions:** You can fork the SDK and add custom methods or enhancements that fit your application needs. But I would greatly appreciate if you could contribute them back to the main repository.
- **Community Support:** Join our community discussions to share improvements and ask for guidance on advanced topics.
For more details on contributing, see the [Contributing Guidelines](https://github.com/filipowm/go-unifi/blob/main/CONTRIBUTING.md).
---
This document is intended for advanced users who need deeper control and customization over the UniFi client.
For most users, the basic configuration and usage examples should suffice.

78
docs/codegen.md Normal file
View File

@@ -0,0 +1,78 @@
# Code Generation for UniFi Go SDK
The UniFi Go SDK uses a code generation process to create the client code, data models, and REST methods from JSON API
specifications. This documentation explains how to run the code generator, available parameters, and how to customize
the output using the `customizations.yml` file.
## Overview
The code generator reads the UniFi Controller API specifications (typically extracted from the UniFi Controller JAR
or JSON files) and generates Go code that includes data models, REST methods, and validation logic. The generation
process uses templates (such as `api.go.tmpl` or `client.go.tmpl`) and configuration files.
## Running the Code Generator
There are two common ways to run the code generator from the command line:
### 1. Using `go generate`
The recommended way to regenerate the client code is to use the `go generate` command. This command looks for special
comments in the source code (usually in a file such as `unifi/codegen.go`) and runs the generator as specified.
Simply navigate to the root of the project and execute:
```bash
go generate unifi/codegen.go
```
This command will:
- Parse the API specifications
- Apply the templates to generate updated models and REST methods
- Overwrite the generated files with the latest code
### 2. Running the Generator Directly with `go run`
Alternatively, you can run the code generator directly using the `go run` command. This approach bypasses `go generate`
and directly executes the generator file. For example:
```bash
go run ./codegen [OPTIONS] version
```
This command performs the same actions as the `go generate` command, but allows you to customize passed parameters as
described in the next section.
## Options
The code generator accepts the following flags, which control its behavior. These parameters can be set via command-line
flags when running the generator, either using `go run ./codegen` or through `go generate` (if configured accordingly):
| Flag | Description | Default Value |
|---------------------|----------------------------------------------------------------------------|---------------|
| `-version-base-dir` | The base directory for version JSON files | `.` |
| `-output-dir` | The output directory of the generated Go code | `.` |
| `-download-only` | Only download and build the API structures JSON directory; do not generate | |
| `-debug` | Enable debug logging | |
| `-trace` | Enable trace logging (takes precedence over `-debug` flag) | |
*Note:* These flags are defined in the main function of the code generator (see `codegen/main.go`). For further details,
consult the source code comments in that file.
## Customizing Code Generation with customizations.yml
The `customizations.yml` file is used to fine-tune the code generation process. It can be used to:
- **Override Default Mappings**: Change how certain API fields or types are mapped to Go types.
- **Exclude or Modify Endpoints**: Disable generation for specific API endpoints or alter their behavior.
### How to Use customizations.yml
1. **Edit the File**: Open `codegen/customizations.yml` in your favorite editor and modify the settings according to your needs.
The file typically contains sections for field mappings, endpoint customizations, and additional template directives.
2. **Run the Generator**: When you run the generator (using either `go generate` or `go run ./codegen`),
it will read the customizations and apply them to the generated code.
3. **Review the Output**: Check the generated files to ensure that your customizations have been applied as expected.
Adjust the `customizations.yml` file and re-run the generator if further changes are needed.

205
docs/configuration.md Normal file
View File

@@ -0,0 +1,205 @@
# Client Configuration
The UniFi Go SDK client is highly configurable to cater to different needs and environments. This document explains the various configuration options available in the client.
## Authentication Methods
The client supports two authentication protocols:
### API Key Authentication
Use this method for better security and dedicated access. Example:
```go
c, err := unifi.NewClient(&unifi.ClientConfig{
BaseURL: "https://unifi.localdomain",
APIKey: "your-api-key",
})
if err != nil {
log.Fatalf("Error creating client: %v", err)
}
```
### Username/Password Authentication
Alternatively, you can use username and password:
```go
c, err := unifi.NewClient(&unifi.ClientConfig{
BaseURL: "https://unifi.localdomain",
Username: "your-username",
Password: "your-password",
})
if err != nil {
log.Fatalf("Error creating client: %v", err)
}
```
## Validation Modes
The client has three modes of validation for the API models. The modes help to ensure that the data sent to the controller is correct.
- **Soft Validation (`unifi.SoftValidation`)**: Logs warnings for invalid fields, but does not fail the request (default).
- **Hard Validation (`unifi.HardValidation`)**: Returns an error for invalid fields, preventing the request from being sent.
- **Disable Validation (`unifi.DisableValidation`)**: Disables all validations.
Configure the validation mode as follows:
```go
c, err := unifi.NewClient(&unifi.ClientConfig{
BaseURL: "https://unifi.localdomain",
APIKey: "your-api-key",
ValidationMode: unifi.HardValidation,
})
if err != nil {
log.Fatalf("Error creating client: %v", err)
}
```
## Customizing the HTTP Client
You can provide your own HTTP client configuration using the `HttpCustomizer` callback. This is useful if you need to tweak connection settings like timeouts, idle connection settings, or TLS configurations:
```go
c, err := unifi.NewClient(&unifi.ClientConfig{
BaseURL: "https://unifi.localdomain",
APIKey: "your-api-key",
HttpCustomizer: func(transport *http.Transport) error {
transport.MaxIdleConns = 10
// Customize TLS settings, proxy, etc. as needed
return nil
},
})
if err != nil {
log.Fatalf("Error creating client: %v", err)
}
```
## Using Interceptors
Interceptors let you hook into the request and response flow. They can be used for logging, metrics, or modifying requests/responses.
Implement the [ClientInterceptor](https://pkg.go.dev/github.com/filipowm/go-unifi/unifi#ClientInterceptor) interface:
```go
// LoggingInterceptor logs each request and response
type LoggingInterceptor struct{}
func (l *LoggingInterceptor) InterceptRequest(req *http.Request) error {
log.Printf("Request: %s %s", req.Method, req.URL)
return nil
}
func (l *LoggingInterceptor) InterceptResponse(resp *http.Response) error {
log.Printf("Response status: %d", resp.StatusCode)
return nil
}
c, err := unifi.NewClient(&unifi.ClientConfig{
BaseURL: "https://unifi.localdomain",
APIKey: "your-api-key",
Interceptors: []unifi.ClientInterceptor{&LoggingInterceptor{}},
})
if err != nil {
log.Fatalf("Error creating client: %v", err)
}
```
This flexibility allows you to modify client behavior to suit your application's needs.
## Comprehensive Client Configuration Example
The `ClientConfig` struct is the central configuration for initializing the UniFi client. It allows you to
fine-tune every aspect of the client's behavior such as the controller URL, authentication credentials, HTTP timeout,
SSL verification, custom HTTP transport settings, interceptors, error handling, concurrency locking, and request validation modes.
Below is a full example demonstrating how to configure and use all available properties of `ClientConfig` when
initializing the client with `unifi.NewClient`:
```go
package main
import (
"context"
"crypto/tls"
"fmt"
"log"
"net/http"
"time"
"github.com/filipowm/go-unifi/unifi"
)
// customTransportCustomizer customizes the HTTP transport, e.g., setting idle connection limits and TLS options.
func customTransportCustomizer(transport *http.Transport) error {
transport.MaxIdleConns = 50
transport.IdleConnTimeout = 120 * time.Second
// Set a custom TLS configuration
transport.TLSClientConfig = &tls.Config{
MinVersion: tls.VersionTLS12,
}
return nil
}
// myErrorHandler implements a custom error handler for HTTP responses.
type myErrorHandler struct{}
func (h *myErrorHandler) HandleError(resp *http.Response) error {
if resp.StatusCode >= 400 {
return fmt.Errorf("custom error: received status code %d", resp.StatusCode)
}
return nil
}
// customInterceptor is a simple interceptor that adds a custom header to each request.
type customInterceptor struct{}
func (ci *customInterceptor) InterceptRequest(req *http.Request) error {
req.Header.Set("X-Custom-Header", "CustomValue")
return nil
}
func (ci *customInterceptor) InterceptResponse(resp *http.Response) error {
// Additional response processing can be added here if needed.
return nil
}
func main() {
// Create a comprehensive client configuration.
config := &unifi.ClientConfig{
URL: "https://unifi.example.com", // Base URL of the UniFi controller (without trailing '/api')
APIKey: "your-api-key", // API key for authentication. Alternatively, use User and Pass for user/password auth.
// User: "username", // Uncomment and provide if using user/password authentication
// Password: "password", // Uncomment and provide if using user/password authentication
Timeout: 30 * time.Second, // Maximum duration to wait for a response
VerifySSL: true, // Enable SSL certificate verification
Interceptors: []unifi.ClientInterceptor{&customInterceptor{}}, // Custom interceptors for request/response manipulation
HttpCustomizer: customTransportCustomizer, // Function to customize the underlying HTTP transport
UserAgent: "MyCustomAgent/1.0", // Custom User-Agent string
ErrorHandler: &myErrorHandler{}, // Custom error handler for processing HTTP response errors
UseLocking: true, // Enable internal locking for safe concurrent request processing
ValidationMode: unifi.SoftValidation, // Validation mode: SoftValidation, HardValidation, or DisableValidation
}
// Initialize the UniFi client with the specified configuration.
client, err := unifi.NewClient(config)
if err != nil {
log.Fatalf("Error creating UniFi client: %v", err)
}
// Example operation: Retrieve system information from the UniFi controller.
sysInfo, err := client.GetSystemInformation()
if err != nil {
log.Fatalf("Error retrieving system information: %v", err)
}
log.Printf("Connected to UniFi Controller version: %s", sysInfo.Version)
// Further client operations can be performed using the 'client' instance.
// For example: creating networks, retrieving device information, etc.
}
```
This example demonstrates how to utilize the full range of configuration options provided by `ClientConfig` to create
a highly customizable UniFi client.

116
docs/getting_started.md Normal file
View File

@@ -0,0 +1,116 @@
# Getting Started with UniFi Go SDK
This guide will help you get started with the UniFi Go SDK client. It covers prerequisites, installation, and basic client initialization.
I highly recommend to use the latest version of UniFi Go SDK, as well as update your UniFi Controller to the latest version to ensure compatibility.
## Prerequisites
- Go 1.16 or later
## Installation
Install the UniFi Go SDK by running:
```bash
go get github.com/filipowm/go-unifi
```
If you need to regenerate the client code from the API specifications, run:
```bash
go generate unifi/codegen.go
```
## Initialization
The client supports both API Key authentication and username/password authentication. Below are examples for both methods.
Unifi client support both username/password and API Key authentication. It is recommended to use API Key authentication
together with dedicated user restricted to local access only.
**IMPORTANT:** API Key authentication is available only since UniFi Controller version 9.0.108
### Obtaining an API Key
1. Open your Site in UniFi Site Manager
2. Click on Control Plane -> Admins & Users.
3. Select your Admin user.
4. Click Create API Key.
5. Add a name for your API Key.
6. Copy the key and store it securely, as it will only be displayed once.
7. Click Done to ensure the key is hashed and securely stored.
8. Use the API Key 🎉
### API Key Authentication
```go
c, err := unifi.NewClient(&unifi.ClientConfig{
BaseURL: "https://unifi.localdomain",
APIKey: "your-api-key",
})
if err != nil {
log.Fatalf("Failed to create client: %v", err)
}
```
### Username/Password Authentication
```go
c, err := unifi.NewClient(&unifi.ClientConfig{
BaseURL: "https://unifi.localdomain",
Username: "your-username",
Password: "your-password",
})
if err != nil {
log.Fatalf("Failed to create client: %v", err)
}
```
### Bare Client Initialization
You can also use bare client, which creates a `unifi.Client` without initialization like logging in and getting system information. This can be useful in specific scenarios, when doing such initialization might be an uneeded overhead. To create it you can use the `NewBareClient` function provided in the SDK (see `unifi/client.go`).
Example usage:
```go
c, err := unifi.NewBareClient(&unifi.ClientConfig{
BaseURL: "https://unifi-controller.example.com",
APIKey: "your-api-key", // or use Username/Password as needed
// Configuration for a bare client
})
if err != nil {
log.Fatalf("Error creating bare client: %v", err)
}
err = c.Login()
if err != nil {
log.Fatalf("Error logging in: %v", err)
}
```
## Generating Client Code
The UniFi Go SDK uses code generation to provide complete API coverage. To regenerate the client based on the latest specifications, run:
```bash
go generate unifi/codegen.go
```
This will update the generated models and REST methods according to the current UniFi Controller API specifications.
## Usage
Once instantiated, the Bare Client provides direct access to the generated API methods. You can perform operations without the extra layers of processing provided by interceptors or custom validations.
If you use a default site and didn't create any new ones, you can use the `default` site ID.
**Example:**
```go
networks, err := c.ListNetwork(ctx, "default")
if err != nil {
log.Fatalf("Error listing networks: %v", err)
}
for _, network := range networks {
fmt.Printf("Network: %s\n", network.Name)
}
```

15
docs/readme.md Normal file
View File

@@ -0,0 +1,15 @@
# UniFi Client Documentation - Overview
Welcome to the in-depth documentation for the UniFi Go SDK client.
This client provides a robust, strongly-typed interface to interact with the UniFi Network Controller API,
leveraging auto-generated models and manual enhancements for undocumented endpoints.
## Table of Contents
- [Overview](readme)
- [Getting Started](getting_started.md)
- [Client Configuration](configuration.md)
- [Usage Examples](usage_examples.md)
- [Advanced Topics](advanced_topics.md)
Each section is designed to help you understand and effectively utilize the UniFi client in your Go projects.

55
docs/usage_examples.md Normal file
View File

@@ -0,0 +1,55 @@
# Usage Examples
This document demonstrates several common usage scenarios for the UniFi Go SDK client.
## Listing Networks
List all available networks in a given site:
```go
networks, err := c.ListNetwork(ctx, "site-name")
if err != nil {
log.Fatalf("Error listing networks: %v", err)
}
for _, network := range networks {
fmt.Printf("Network: %s\n", network.Name)
}
```
## Creating a User
Create a new user assigned to the first available network:
```go
// Assume networks have been retrieved as shown above
user, err := c.CreateUser(ctx, "site-name", &unifi.User{
Name: "My Network User",
MAC: "00:00:00:00:00:00",
NetworkID: networks[0].ID,
IP: "10.0.21.37",
})
if err != nil {
log.Fatalf("Error creating user: %v", err)
}
fmt.Printf("Created user: %s\n", user.Name)
```
## Updating a Guest Access setting
Update the guest access setting for a network:
```go
setting := &unifi.SettingGuestAccess{
PortalCustomizedBoxColor: "#ff0000",
PortalCustomizedSuccessText: "Welcome to the network!",
PortalCustomized: true,
}
setting, err = c.UpdateSettingGuestAccess(ctx, "site-name", setting)
if err != nil {
log.Fatalf("Error updating guest access setting: %v", err)
}
// Use the updated setting
```

View File

@@ -48,8 +48,8 @@ Fields:
URL: The base URL of the UniFi controller. Must be a valid URL and should not include the `/api` suffix.
APIKey: An API key used for authentication. Provide this if user/password credentials are not used.
User: The username for user/password authentication. Must be provided with Pass if APIKey is not used.
Pass: The password for user/password authentication. Must be provided with User if APIKey is not used.
User: The username for user/password authentication. Must be provided with Password if APIKey is not used.
Password: The password for user/password authentication. Must be provided with User if APIKey is not used.
Timeout: The maximum duration to wait for responses; default is no timeout.
VerifySSL: When false, disables SSL certificate verification.
Interceptors: A slice of ClientInterceptor implementations that can modify requests and responses.
@@ -61,9 +61,9 @@ Fields:
*/
type ClientConfig struct {
URL string `validate:"required,http_url"`
APIKey string `validate:"required_without_all=User Pass"`
User string `validate:"excluded_with=APIKey,required_with=Pass"`
Pass string `validate:"excluded_with=APIKey,required_with=User"`
APIKey string `validate:"required_without_all=User Password"`
User string `validate:"excluded_with=APIKey,required_with=Password"`
Password string `validate:"excluded_with=APIKey,required_with=User"`
Timeout time.Duration // How long to wait for replies, default: forever.
VerifySSL bool
Interceptors []ClientInterceptor
@@ -99,14 +99,14 @@ func (a APIKeyCredentials) GetPass() string { return "" }
// UserPassCredentials holds user/password authentication.
type UserPassCredentials struct {
User string
Pass string
User string
Password string
}
func (u UserPassCredentials) IsAPIKey() bool { return false }
func (u UserPassCredentials) GetAPIKey() string { return "" }
func (u UserPassCredentials) GetUser() string { return u.User }
func (u UserPassCredentials) GetPass() string { return u.Pass }
func (u UserPassCredentials) GetPass() string { return u.Password }
// client represents a UniFi client.
type client struct {
@@ -185,7 +185,7 @@ func newClientFromConfig(config *ClientConfig, v *validator) (*client, error) {
credentials = APIKeyCredentials{APIKey: config.APIKey}
interceptors = append(interceptors, &APIKeyAuthInterceptor{apiKey: config.APIKey})
} else {
credentials = UserPassCredentials{User: config.User, Pass: config.Pass}
credentials = UserPassCredentials{User: config.User, Password: config.Password}
interceptors = append(interceptors, &CSRFInterceptor{})
}
if len(config.UserAgent) == 0 {

View File

@@ -52,7 +52,7 @@ func TestNewBareClient(t *testing.T) {
c, err := newBareClient(&ClientConfig{
URL: localUrl,
User: "admin",
Pass: "password",
Password: "password",
VerifySSL: false,
})
require.Error(t, err)
@@ -314,7 +314,7 @@ func TestUnifiIntegrationUserPassInjected(t *testing.T) {
c, _ := newBareClient(&ClientConfig{
URL: srv.URL,
User: "test-user",
Pass: "test-pass",
Password: "test-pass",
Interceptors: interceptor.AsList(),
})
c.apiPaths = &NewStyleAPI
@@ -360,7 +360,7 @@ func TestCsrfHandling(t *testing.T) {
c, _ := newBareClient(&ClientConfig{
URL: srv.URL,
User: "test-user",
Pass: "test-pass",
Password: "test-pass",
Interceptors: interceptor.AsList(),
})
c.apiPaths = &NewStyleAPI
@@ -425,10 +425,10 @@ func TestAuthConfigurationValidation(t *testing.T) {
t.Parallel()
// given
cc := &ClientConfig{
URL: testUrl,
User: tc.User,
Pass: tc.Pass,
APIKey: tc.APIKey,
URL: testUrl,
User: tc.User,
Password: tc.Pass,
APIKey: tc.APIKey,
}
// when