New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Provide Go client library #141
Conversation
a2f9202
to
6afa71c
Compare
_ = &Change{Path: "/a.json", Type: "APPLY_JSON_PATCH", Content: content} | ||
|
||
// TODO(minwoox) fix this to work | ||
//if !reflect.DeepEqual(entry, want) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will you fix this in this PR or?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed~!!
{Path: "/b.txt", Type: "APPLY_TEXT_PATCH", Content: "--- /b.txt\n+++ /b.txt\n@@ -1,1 +1,1 @@\n-foo\n+bar"}} | ||
|
||
// TODO(minwoox) fix this to work | ||
//if !reflect.DeepEqual(changes, want) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto
client/go/dogma/dogma.go
Outdated
DefaultPort = 36462 | ||
DefaultPathPrefix = "api/v1/" | ||
PathPrefixV0 = "api/v0/" | ||
DefaultBaseURL = "http://localhost:36462/" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Just curious - can we construct this from
DefaultScheme
,DefaultHostName
andDefaultPort
? - Perhaps we could make all constants here except
DefaultPort
hidden from the public API?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. Thank you ~!
I thought I couldn't make it because strconv.Itoa(port)
didn't work. But I found that I can simply do string(port)
.
client/go/dogma/dogma.go
Outdated
return NewClient(baseURL, config.Client(context.Background())) | ||
} | ||
|
||
func NewClient(baseURL string, client *http.Client) (*Client, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which would be used more frequently? If it's NewClientWithCredential
, we could rename NewClientWithCredential
to NewClient
and NewClient
to NewClientWithHTTPClient
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. Thank you~!
client/go/dogma/dogma.go
Outdated
defer res.Body.Close() | ||
|
||
if res.StatusCode != http.StatusOK { | ||
return false, fmt.Errorf("authenticaion check is failed (status: %s)", res.Status) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
authentication failed (status: %s)
|
||
private final String refreshToken; | ||
|
||
//TODO(minwoox) add scope when it needs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add scope if needed. ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a part of the response, there is a scope.
scope (optional) If the scope the user granted is identical to the scope the
app requested, this parameter is optional. If the granted scope is different
from the requested scope, such as if the user modified the scope, then
this parameter is required.
in here
But I don't think we need it right now, so just left a comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually meant to update the comment to 'Add scope if needed.' rather than asking to add it in this PR. :-D
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops~ got it.~! Thank you!
|
||
@JsonProperty("expires_in") | ||
public long expiresIn() { | ||
return expiresIn; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we should use expires_at
so that a client knows when exactly a token will be expired. Or is this a part of OAUTH2 specification?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it is a part of the spec. :-)
return new UsernamePasswordToken(username, password); | ||
} | ||
|
||
return throwResponseException("invalid_request", "request was missing the username or password."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
request must contain username and password.
?
* {@link JsonNode}. | ||
*/ | ||
public static HttpResponseException newHttpResponseException(HttpStatus status, JsonNode node) { | ||
return HttpResponseException.of(newResponseWithJson(status, node)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps we should provide a way to specify an HttpStatus
with a response object in Armeria?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was think about adding methods whose signatures are same as HttpResponse.of(...)
, but I was not sure which methods I needed to add to HttpResponseException
, because there are a lot. :-)
To handle above case, I need a method like this.
public static HttpResponseException of(HttpStatus status, MediaType mediaType, byte[] content) {
...
}
or
public static HttpResponseException of(HttpHeaders headers, byte[] content) {
...
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah we should add those methods so that a user can send an error response conveniently. However, I guess a user will find it weird to throw an exception when sending a successful response.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's right. Do we need to check the status here if we add those methods?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess not. You know, some peculiar API uses 200 OK for error responses ;-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, we talked about it in this morning. lol
HttpHeaderNames.AUTHORIZATION, "basic " + encoder.encodeToString( | ||
(USERNAME + ':' + PASSWORD).getBytes(StandardCharsets.US_ASCII)), | ||
HttpHeaderNames.CONTENT_TYPE, MediaType.FORM_DATA.toString()), | ||
"grant_type=client_credentials", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be password
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From the Golang oauth2 client, I thought it was "grant_type=client_credentials"
.
But I checked again the spec, and it actually does not require the grant_type
.
So I will remove it :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought I deleted this review comment but seems like I forgot. :-$
47c9d25
to
8c4065f
Compare
ffb69f3
to
1903c0b
Compare
Codecov Report
@@ Coverage Diff @@
## master #141 +/- ##
=========================================
- Coverage 63.51% 63.5% -0.02%
=========================================
Files 273 273
Lines 9451 9474 +23
Branches 1065 1063 -2
=========================================
+ Hits 6003 6016 +13
- Misses 2825 2834 +9
- Partials 623 624 +1
Continue to review full report at Codecov.
|
client/go/dogma/dogma.go
Outdated
PathPrefixV0 = "api/v0/" | ||
DefaultBaseURL = DefaultScheme + "://" + DefaultHostName + ":36462/" | ||
|
||
defaultPort = 36462 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually meant making all constants private, except DefaultPort
. :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops~~ let me fix this~
e178d13
to
9781a8e
Compare
client/go/dogma/dogma.go
Outdated
} else { | ||
buf = new(bytes.Buffer) | ||
enc := json.NewEncoder(buf) | ||
//enc.SetEscapeHTML(true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Is this necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's necessary. Forgot to bring it back. Thank you!
@@ -60,14 +71,13 @@ public LoginService(CentralDogmaSecurityManager securityManager, CommandExecutor | |||
protected HttpResponse doPost(ServiceRequestContext ctx, HttpRequest req) throws Exception { | |||
final CompletableFuture<HttpResponse> future = new CompletableFuture<>(); | |||
req.aggregate().thenAccept(aMsg -> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now we can use AggregatedHttpMessage
on the parameter list. So, how about changing HttpRequest
to AggregatedHttpMessage
and removing the aggregation from here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed! Thank you~!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops I realized that this class inherits the AbstractHttpService
and the method overrides, so I can not change :-)
As described in #59, we are going to remove Thrift :) |
client/go/dogma/content_service.go
Outdated
} | ||
|
||
func (con *contentService) getFiles(ctx context.Context, | ||
projectName, repoName, revision, pathPattern string) ([]*Entry, *http.Response, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there any chance that user would care about http.Response? Adding response will make the API couple to http package.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good question~! This is the one that I barely made a decision.
Some go client libraries such as go-github that I referred, returns the *http.Response
.
I was thinking that a user may want to take a look at the response when something unexpected happens.
And I made the constructor for client which takes the *http.Client
as an argument. So I though t is was OK. If the user does not want it, the user can just ignore it with _
.
But I think I can take that out as well. :-)
Do you think it is too tight with the http package?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Leave few nits comments.
One more question: as a user I want something like Watcher
in java client, is it possible without Thrift or we need to make some REST API for polling?
client/go/dogma/content_service.go
Outdated
|
||
if len(revision) != 0 { | ||
u += "?revision=" + revision | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how about use url#Values pacakge to format
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In order to do that, parse the string as url.URL
and change it back to string when it invokes newRequest()
.
That's why I was reluctant to use the package, but probably it's better use it. :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, I totally misunderstood the API. Thank you for letting me know that.
client/go/dogma/content_service.go
Outdated
u := fmt.Sprintf("%vprojects/%v/repos/%v/commits/%v", defaultPathPrefix, projectName, repoName, from) | ||
|
||
urlQuery := "" | ||
if len(pathPattern) != 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto (using url#Values instead)
client/go/dogma/content_service.go
Outdated
|
||
type Push struct { | ||
CommitMessage *CommitMessage `json:"commitMessage"` | ||
Changes []Change `json:"changes"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how about use []*Change instead for uniformity
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good idea.
Is there any convenient way to convert the []Change
to []*Change
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm afraird not :(
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just changed the method parameter to take []*Change
.
I realized that I don't have to take the argument as varargs because I need at least one Change
.
Thank you~!
|
||
// CreateProject creates a project. | ||
func (c *Client) CreateProject(ctx context.Context, name string) (*Project, *http.Response, error) { | ||
return c.project.create(ctx, name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure what is better, but compare between below 2 interfaces, I prefer former one. HDYT?
// expose service object
dogma.Project.Create(...)
vs
// current approach
dogma.CreateProject(...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Either way is fine to me. Just wanted to have same method name as in Java client library :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same method name as in Java client
this sound reasonable, so let's keep compability for emthod naming rule
client/go/dogma/dogma.go
Outdated
import "golang.org/x/oauth2/clientcredentials" | ||
|
||
func Client() { | ||
config := clientcredentials.Config{ClientID: clientID, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we support oauth only and not basic auth?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This authClient actually uses basic auth when it requests a token. But I think I need to change the example because this can confuse :-)
I am planning to make the |
Motivation: If we provide Go client library, more people can adapt Central Dogma. Modifications: - Add Go client library Result: - More customers Todos: - Repackage the original sources - Adapt CLI using this library - Adapt OAuth2 client - Add the watcher
@huydx , Fixed the issues, please take a look. |
client/go/dogma/content_service.go
Outdated
// QueryType can be "identity" or "json_path". "identity" is used to retrieve the content as it is. | ||
// "json_path" applies a series of JSON path to the content. | ||
// See https://github.com/json-path/JsonPath/blob/master/README.md | ||
QueryType string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sorry I missed that at first round, do you think string is better than iota?
Using iota may need some more boilerplate code to generate equivalent string, but for users it's easier to use because they know that what type of QueryType is supported, and prevent typo as well.
In case you find it using iota will add too much boiler plate, how about define some constant for those strings?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
iota
should be much better~! Thank you~!
client/go/dogma/content_service.go
Outdated
// Entry represents an entry in the repository. | ||
type Entry struct { | ||
Path string `json:"path"` | ||
Type string `json:"type"` // can be JSON, TEXT or DIRECTORY |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same with above comment about iota
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This field is used to store the JSON response. If I use this as iota
, I need a custom deserializer to convert the sting to corresponding iota number.
Can I implement the custom deserializer or is it better to use predefined string constants?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a user, I would prefer iota constant. IMHO it's fine to add deserialize function like func (o *Type) UnmarshalJSON(b []byte) error {}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it. I will try the way you suggest. Thang you!
client/go/dogma/content_service.go
Outdated
Path string `json:"path"` | ||
|
||
// can be UPSERT_JSON, UPSERT_TEXT, REMOVE, RENAME, APPLY_JSON_PATCH or APPLY_TEXT_PATCH | ||
Type string `json:"type"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same with above comment about iota
client/go/dogma/dogma.go
Outdated
return newClientWithHTTPClient(normalizedURL.String(), config.Client(context.Background())) | ||
} | ||
|
||
// newClientWithHTTPClient returns a Central Dogma client withe the specified baseURL and client. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo withe
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks~!
client/go/dogma/dogma.go
Outdated
} | ||
|
||
// NewClient returns a Central Dogma client with the specified baseURL, clientID and clientSecret. | ||
func NewClient(baseURL string, clientID, clientSecret string) (*Client, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how about 2 separate constructors like
NewClient(baseUrl string) *Client, error {
}
NewSecureClient(baseURL string, clientID, clientSecret string) *Client, error {
}
And in inner implement we will check if clientID and clientSecret is empty, we will use secure API and opposite?
Not sure about the spec of CD server right now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think having just one constructor is fine for now so that the client does not have to handle the authentication by itself. Let's add it when we think it really needs :-)
@minwoox added second round comments |
@huydx Please take a look again~. I was trying to use some library like enumer to convert the iota values, but it seems like I coudln't not have different names for the enum. For example, we use |
LGTM! Should I merge it @minwoox |
Thanks @huydx. Let's wait a little bit in case @trustin, @hyangtack want to leave a comment. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still LGTM
I misunderstood the Oauth2 spec. I removed the client credentials part. :-) |
client/go/dogma/dogma.go
Outdated
// NewClient returns a Central Dogma client with the specified baseURL, clientID and clientSecret. | ||
func NewClient(baseURL string, clientID, clientSecret string) (*Client, error) { | ||
// NewClient returns a Central Dogma client with the specified baseURL, username and password. | ||
func NewClient(baseURL string, username, password string) (*Client, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Just curious. username
-> username string
? Is the type unnecessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops~! Thank you for pointing it out. (baseURL, username, password string)
is correct because all of the three parameters are string, I can just put one string
at the end. (does not have to put type for every parameter, if they are same)
Thanks reviews~! |
Motivation:
If we provide Go client library, more people can adapt Central Dogma.
Modifications:
Result:
Todos: