corbel-go is an API library for work with corbel. It currently supports:
- Creation of a new Client
- Token workflow
- Basic Authentication (username/password)
- Resources (get/create/update/delete/search)
Note: Library in active development; requires >= Go 1.3
The client is the key to use the platform. All information here is custom for each client since its generated for every application that needs to use it.
Instancing the client allow to use it everywhere. Since Authorization can be for the application itself or its users on dynamic applications will be necessary to have several clients with several authorizations (one for each user) because you will have specific scopes based on your own permissions.
See the Authorization part to get all the possible variations.
// NewClient(http.Client, endpoints, clientId, clientName, clientSecret,
// clientScopes, clientDomain, JWTSigningMethod, tokenExpirationTime)
endpoints := map[string]string{"iam": "https://localhost", "resources": "https://localhost"}
client, _ = NewClient(nil, endpoints, "someID", "", "someSecret", "", "", "HS256", 3000)
Getting token for client app
When you will use the client for application purposes you must ask for a OauthToken to get the specific permissions, that normally are wide open than users.
err = client.IAM.OauthToken()
Getting token for user using basic auth
If the client will be used for operations as user you need validate and get the proper user token. User token will have all the permissions applied to that user that are custom for her.
err = client.IAM.OauthTokenBasicAuth("username", "password")
All actions over users on the domain can be done if the application/user have the required permissions. All user interactions are done using the IAM (Identity and Authorization Management) endpoint.
IAM User definition
All operations for an user must be done using the IAMUser struct.
type IAMUser struct {
ID string `json:"id,omitempty"`
Domain string `json:"domain,omitempty"`
Username string `json:"username,omitempty"`
Email string `json:"email,omitempty"`
FirstName string `json:"firstName,omitempty"`
LastName string `json:"lastName,omitempty"`
ProfileURL string `json:"profileUrl,omitempty"`
PhoneNumber string `json:"phoneNumber,omitempty"`
Scopes []string `json:"scopes,omitempty"`
Properties map[string]interface{} `json:"properties,omitempty"`
Country string `json:"country,omitempty"`
CreatedDate int `json:"createdDate,omitempty"`
CreatedBy string `json:"createdBy,omitempty"`
}
NOTES:
- User properties is a map that allow to add arbitrary information of that user. All JSON serializable types are allowed. Beware of those properties that can be empty, 0, false or nil, since it won't be exported if omitempty are used.
- Scopes can be an empty array if are defined default scopes for users on the domain definition.
- CreatedDate and CreatedBy are managed by the platform itself, so any change there will be ignored.
anUserProperties := make(map[string]interface{})
anUserProperties["string"] = "test string"
anUserProperties["integer"] = 123456
anUserProperties["float"] = 1.23
anUserProperties["date"] = now
anUser := IAMUser{
Domain: "corbel-qa",
Username: "corbel-go",
Email: "corbel-go@corbel.org",
FirstName: "Corbel",
LastName: "Go",
ProfileURL: "http://corbel.org/corbel-go",
PhoneNumber: "555-555-555",
Scopes: []string{},
Properties: anUserProperties,
Country: "Somewhere",
}
err = client.IAM.Add(&anUser)
anUser2 := IAMUser{}
err = client.IAM.Get("sampleId", &anUser2)
currentUser := IAMUser{}
err = client.IAM.GetMe(¤tUser)
anUser.Country = "Internet"
err = client.IAM.Update("sampleId", &anUser)
err = client.IAM.Delete("sampleId")
search := client.IAM.Search()
search.Query.Eq["username"] = "corbel-go"
var arrUsers []IAMUser
err = search.Page(0, &arrUsers)
NOTE: Searching uses the same interface defined in detail on the Resources documentation part.
Adding resource
Adds a resource of a defined type. Definitions are JSON parseable structs.
Important Note: Avoid using omitempty in the JSON definition if you think you could have a value that could turn false, 0, empty strings or nil. In those cases json.Marshal won't export the data. So value won't be updated in the backend. Important Note 2: Is recommended to define the ID on the structs to be able to update them correctly without workarounds.
type ResourceForTest struct {
ID string `json:"id,omitempty"`
Key1 string `json:"key1"`
Key2 int `json:"key2"`
Key3 float64 `json:"key3"`
Key4 bool `json:"key4"`
}
test1 := ResourceForTest{
Key1: "test string",
Key2: 123456,
Key3: 1.123456,
Key4: true,
}
err = client.Resources.AddToCollection("test:GoTestResource", &test1)
Search allow to browse for the required resources using a simple interface. All Search conditions are shared in all modules of corbel, so it's the same for users, for example.
Per Page
var arrResourceForTest []ResourceForTest
search = client.Resources.SearchCollection("test:GoTestResource")
err = search.Page(0, &arrResourceForTest)
Conditions
Searching resources by specifying conditions.
search = client.Resources.SearchCollection("test:GoTestResource")
// all items where firstName == "testName"
search.Query.Eq["firstName"] = "testName"
// sort by firstName
search.Sort.Asc = []string{"firstName"}
// list 20 resources por search page
search.PerPage = 20
err = search.Page(0, &arrResourceForTest)
All allowed search conditions
Eq map[string]string // Equal to
Gt map[string]int // Greater than
Gte map[string]int // Greater than or equal
Lt map[string]int // Less than
Lte map[string]int // Less than or equal
Ne map[string]string // Not Equal
In map[string][]string // One of this array
All map[string][]string // All of this array
Like map[string]string // Like
Sort conditions
Asc []string // Ascendent
Desc []string // Descendent
Aggregations
func (s *Search) Count(field string) (int, error) {}
func (s *Search) CountAll() (int, error) {}
func (s *Search) Average(field string) (float64, error) {}
func (s *Search) Sum(field string) (float64, error) {}
test2 := ResourceForTest{}
err = client.Resources.GetFromCollection("test:GoTestResource",
"1234567890abcdef", &test2)
test2.Key1 = "new string"
err = client.Resources.UpdateInCollection("test:GoTestResource",
"1234567890abcdef", &test2)
err = client.Resources.DeleteFromCollection("test:GoTestResource",
"1234567890abcdef")
Resources can have related resources using collections. As sample think in a Music Group resource that have several Album resources.
Adding a resource to a Collection automatically creates the Colection, so you don't need to be worried on Collection creation. All relations allow to add custom metadata. To query that metadata is posible to create custom metadata structs to use it later.
// Sample without metadata
err = client.Resources.AddRelation("test:MusicGroup", "12345",
"test:Albums",
"test:Album", "23456",
nil)
// Sample with custom metadata
type groupAlbumRelation struct {
RecordLabel string `json:"recordLabel"`
}
metadata := groupAlbumRelation{
RecordLabel: "Sample Record Label",
}
err = client.Resources.AddRelation("test:MusicGroup", "12345",
"test:Albums",
"test:Album", "23456",
metadata)
Resources relation allows to reorder the items to be able to get them in the desired order in searches.
Sample: Items: ["1", "2", "3"] MoveRelation "3", 1 Items: ["3", "1", "2"]
// Move the Item "2" to the first position.
err = client.Resources.MoveRelation("test:ToDoList", "12345",
"test:ToDoListItems",
"test:ToDoItem", "2",
1)
To get relations you can use the RelationData struct if you don't added metadata or extend RelationData with your own data.
// RelationData definition.
type RelationData struct {
Order float64 `json:"_order,omitempty"`
ID string `json:"id,omitempty"`
Links []map[string]interface{} `json:"links, omitempty"`
}
Sample with standard metadata.
var arrRelationData []corbel.RelationData
search = client.Resources.SearchRelation("test:Group", "12345",
"test:Albums")
err = search.Page(0, &arrRelationData)
Sample with custom metadata.
type customRelationData struct {
Order float64 `json:"_order,omitempty"`
ID string `json:"id,omitempty"`
Links []map[string]interface{} `json:"links, omitempty"`
RecordLabel string `json:"recordLabel"`
}
var arrRelationData []customRelationData
search = client.Resources.SearchRelation("test:Group", "12345",
"test:Albums")
err = search.Page(0, &arrRelationData)
Sample with selected ordering.
var arrRelationData []customRelationData
search = client.Resources.SearchRelation("test:Group", "12345",
"test:Albums")
search.Sort.Asc = []string{"_order"}
err = search.Page(0, &arrRelationData)
Searching for resources information does not return the target object itself, it returns a pointer to to plus the custom metadata (if added).
type Album struct {
Title string `json:"title"`
PublicationYear int `json:"publicationYear"`
}
var anAlbum Album
err = client.Resources.GetFromRelationDefinition(arrRelationData[0].ID, &anAlbum)
err = client.Resources.DeleteRelation("test:Group", "12345",
"test:Albums",
"test:Album", "23456")
err = client.Resources.DeleteAllRelations("test:Group", "12345",
"test:Albums")
- Fork it
- Create your feature branch (git checkout -b my-new-feature)
- Commit your changes (git commit -am 'Add some feature')
- Push to the branch (git push origin my-new-feature)
- Create new Pull Request
- If applicable, update the README.md