Skip to content
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

- adds support for raw URLs in request builders #508

Merged
merged 23 commits into from
Aug 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
b33c9ec
- updates typescript generation to add support for raw URLs
baywet Aug 19, 2021
4a4379a
- removes dependency on lodash in abstractions package
baywet Aug 20, 2021
454a5fe
- adds the missing doc comment for set URI method
baywet Aug 20, 2021
29bd016
- adds support for raw urls in dotnet
baywet Aug 20, 2021
91f3246
- minor defenseive programing adjustements
baywet Aug 20, 2021
9e03e3d
- adds set URI method to java abstractions
baywet Aug 20, 2021
208ea74
- code linting
baywet Aug 23, 2021
d1ac70a
- updates java generation to support raw url
baywet Aug 23, 2021
caff9a9
- fixes a bug in java abstractions where URL parsing would not work a…
baywet Aug 23, 2021
9e1a0e1
- adds constructor overload in java for default parameter
baywet Aug 23, 2021
d896a92
- adds set uri method to go abstractions
baywet Aug 23, 2021
2cc60d1
- adds support for raw URLs in go
baywet Aug 23, 2021
7bdb2c3
- code-linting
baywet Aug 23, 2021
20721fd
- updates go method writer unit test
baywet Aug 23, 2021
e55a02a
- adds the set URI method for ruby
baywet Aug 23, 2021
1eb43db
- adds support for raw URL in ruby
baywet Aug 23, 2021
9b93477
- fixes a bug where ruby api client would import wrong default serial…
baywet Aug 23, 2021
2eba6b7
- adds changelog entry for raw URLs
baywet Aug 23, 2021
77198e0
- adds tests for code parameters comparer
baywet Aug 24, 2021
c25efa7
- adds unit tests for common language convention service
baywet Aug 24, 2021
4c9df70
- updates unit test for request generators
baywet Aug 24, 2021
12c4231
- bumps patch version for abstractions
baywet Aug 24, 2021
ada8b75
Apply suggestions from code review
baywet Aug 25, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixes a bug where generated models would be tied to a specific backing store implementation #400
- Fixed #428 a bug where inline double defintion would make code dom generation fail
- Revamped authentication provider interface to allow multiple authentication schemes #498
- Fixed a bug preventing from using request builders with raw URls #508

## [0.0.7] - 2021-08-04

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<TargetFramework>net5.0</TargetFramework>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<RepositoryUrl>https://github.com/microsoft/kiota</RepositoryUrl>
<Version>1.0.17</Version>
<Version>1.0.18</Version>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<!-- Enable this line once we go live to prevent breaking changes -->
Expand Down
24 changes: 24 additions & 0 deletions abstractions/dotnet/src/RequestInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,30 @@ public class RequestInfo
/// </summary>
public Uri URI { get; set; }
/// <summary>
/// Sets the URI of the request.
/// </summary>
/// <param name="currentPath">the current path (scheme, host, port, path, query parameters) of the request.</param>
/// <param name="pathSegment">the segment to append to the current path.</param>
/// <param name="isRawUrl">whether the path segment is a raw url. When true, the segment is not happened and the current path is parsed for query parameters.</param>
/// <exception cref="UriFormatException">Thrown when the built URI is an invalid format.</exception>
public void SetURI(string currentPath, string pathSegment, bool isRawUrl)
{
if (isRawUrl)
{
if(string.IsNullOrEmpty(currentPath))
throw new ArgumentNullException(nameof(currentPath));
var parseUri = new Uri(currentPath);
foreach(var qsp in parseUri.Query.Split('&').Select(x => x.Split('=')).Where(x => !string.IsNullOrEmpty(x[0]))) {
QueryParameters.Add(qsp[0], qsp.Length > 1 ? qsp[1] : null);
}
URI = new Uri(parseUri.GetComponents(UriComponents.SchemeAndServer | UriComponents.Path, UriFormat.Unescaped));
}
else
{
URI = new Uri(currentPath + pathSegment);
}
}
/// <summary>
/// The <see cref="HttpMethod">HTTP method</see> of the request.
/// </summary>
public HttpMethod HttpMethod { get; set; }
Expand Down
44 changes: 44 additions & 0 deletions abstractions/go/request_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package abstractions
import (
"errors"
"reflect"
"strings"

"net/url"
u "net/url"

s "github.com/microsoft/kiota/abstractions/go/serialization"
Expand All @@ -19,6 +21,48 @@ type RequestInfo struct {
options map[string]MiddlewareOption
}

func NewRequestInfo() *RequestInfo {
return &RequestInfo{
URI: u.URL{},
Headers: make(map[string]string),
QueryParameters: make(map[string]string),
options: make(map[string]MiddlewareOption),
}
}

func (request *RequestInfo) SetUri(currentPath string, pathSegment string, isRawUrl bool) error {
if isRawUrl {
if currentPath == "" {
return errors.New("current path cannot be empty")
}
questionMarkSplat := strings.Split(currentPath, "?")
schemeHostAndPath := questionMarkSplat[0]
uri, err := url.Parse(schemeHostAndPath)
if err != nil {
return err
}
request.URI = *uri
if len(questionMarkSplat) > 1 {
queryParameters := questionMarkSplat[1]
for _, queryParameter := range strings.Split(queryParameters, "&") {
keyValue := strings.Split(queryParameter, "=")
if len(keyValue) == 2 {
request.QueryParameters[keyValue[0]] = keyValue[1]
} else if len(keyValue) == 1 {
request.QueryParameters[keyValue[0]] = ""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there value in adding the query parameter in the event that the value is absent/null? (I think this applies to all the languages)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought this explanation was the best 🤣

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤣 🤣 🤣
If we can, we should probably remember to upvote that answer

}
}
}
} else {
uri, err := url.Parse(currentPath + pathSegment)
if err != nil {
return err
}
request.URI = *uri
}
return nil
}

func (request *RequestInfo) AddMiddlewareOptions(options ...MiddlewareOption) error {
if options == nil {
return errors.New("MiddlewareOptions cannot be nil")
Expand Down
2 changes: 1 addition & 1 deletion abstractions/java/lib/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ publishing {
publications {
gpr(MavenPublication) {
artifactId 'kiota-abstractions'
version '1.0.17'
version '1.0.18'
from(components.java)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.microsoft.kiota;

import java.net.URI;
import java.net.URISyntaxException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
Expand All @@ -21,6 +22,41 @@ public class RequestInfo {
/** The URI of the request. */
@Nullable
public URI uri;
/**
* Sets the URI of the request.
* @param currentPath the current path (scheme, host, port, path, query parameters) of the request.
* @param pathSegment the segment to append to the current path.
* @param isRawUrl whether the path segment is a raw url. When true, the segment is not happened and the current path is parsed for query parameters.
*/
public void setUri(@Nullable final String currentPath, @Nullable final String pathSegment, final boolean isRawUrl) {
if (isRawUrl) {
if(currentPath == null || currentPath.isEmpty()) {
throw new IllegalArgumentException("currentPath cannot be null or empty");
}
final var questionMarkSplat = currentPath.split("\\?");
final var schemeHostAndPath = questionMarkSplat[0];
this.setUriFromString(schemeHostAndPath);
if (questionMarkSplat.length > 1) {
final var queryString = questionMarkSplat[1];
final var rawQueryParameters = queryString.split("&");
for (var queryParameter : rawQueryParameters) {
final var queryParameterNameValue = queryParameter.split("=");
if (!queryParameterNameValue[0].isEmpty()) {
this.queryParameters.put(queryParameterNameValue[0], queryParameterNameValue.length > 1 ? queryParameterNameValue[1] : null);
}
}
}
} else {
this.setUriFromString(currentPath + pathSegment);
}
}
private void setUriFromString(final String uriString) {
try {
this.uri = new URI(uriString);
} catch (final URISyntaxException e) {
throw new RuntimeException(e);
}
}
/** The HTTP method for the request */
@Nullable
public HttpMethod httpMethod;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,14 @@
import static org.junit.jupiter.api.Assertions.*;

class RequestInfoTest {
@Test void testSomeLibraryMethod() {
RequestInfoTest classUnderTest = new RequestInfoTest();
assertNotNull(classUnderTest);
@Test
void setsRawUri() {
final var requestInfo = new RequestInfo();
requestInfo.setUri("https://graph.microsoft.com/test", null, true);
assertEquals("https://graph.microsoft.com/test", requestInfo.uri.toString());

requestInfo.setUri("https://graph.microsoft.com/test?qp=one", null, true);
assertEquals("https://graph.microsoft.com/test", requestInfo.uri.toString());
assertEquals("one", requestInfo.queryParameters.get("qp"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,30 @@ def headers
@headers ||= Hash.new
end

def set_uri(current_path, path_segment, is_raw_url)
if is_raw_url
if current_path.nil? || current_path.empty?
raise ArgumentError, 'current_path cannot be nil or empty'
end
question_mark_splat = current_path.split(/\?/)
scheme_host_and_path = question_mark_splat[0]
if question_mark_splat.length > 1
query_parameters = question_mark_splat[1]
query_parameters.split(/&/).each do |query_parameter|
key_value_pair = query_parameter.split(/=/)
if key_value_pair.length > 1
query_parameters[key_value_pair[0]] = key_value_pair[1]
elsif key_value_pair.length == 1
query_parameters[key_value_pair[0]] = nil
end
end
end
@uri = URI(current_path)
else
@uri = URI(current_path + path_segment)
end
end

def set_stream_content(value = $stdin)
@content = value
@headers[@@content_type_header] = @@binary_content_type
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module MicrosoftKiotaAbstractions
VERSION = "0.1.4"
VERSION = "0.1.5"
end
13 changes: 1 addition & 12 deletions abstractions/typescript/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions abstractions/typescript/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@microsoft/kiota-abstractions",
"version": "1.0.17",
"version": "1.0.18",
"description": "Core abstractions for kiota generated libraries in TypeScript and JavaScript",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand All @@ -23,7 +23,6 @@
},
"homepage": "https://github.com/microsoft/kiota#readme",
"devDependencies": {
"@types/lodash": "^4.14.172",
"@types/node": "^16.7.1",
"@types/uuid": "^8.3.1",
"typescript": "^4.3.5"
Expand All @@ -32,7 +31,6 @@
"registry": "https://npm.pkg.github.com"
},
"dependencies": {
"lodash": "^4.17.21",
"uuid": "^8.3.2",
"web-streams-polyfill": "^3.1.0"
}
Expand Down
29 changes: 28 additions & 1 deletion abstractions/typescript/src/requestInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,39 @@ import { MiddlewareOption } from "./middlewareOption";
export class RequestInfo {
/** The URI of the request. */
public URI?: string;
/**
* Sets the URI of the request.
* @param currentPath the current path (scheme, host, port, path, query parameters) of the request.
* @param pathSegment the segment to append to the current path.
* @param isRawUrl whether the path segment is a raw url. When true, the segment is not happened and the current path is parsed for query parameters.
*/
public setUri(currentPath: string, pathSegment: string, isRawUrl: boolean) : void {
if(isRawUrl) {
const questionMarkSplat = currentPath.split('?');
const schemeHostAndPath = questionMarkSplat[0];
this.URI = schemeHostAndPath;
if(questionMarkSplat.length > 1) {
const queryString = questionMarkSplat[1];
queryString?.split('&').forEach(queryPair => {
const keyValue = queryPair.split('=');
if(keyValue.length > 0) {
const key = keyValue[0];
if(key) {
this.queryParameters.set(key, keyValue.length > 1 ? keyValue[1] : undefined);
}
}
});
}
} else {
this.URI = currentPath + pathSegment;
}
}
/** The HTTP method for the request */
public httpMethod?: HttpMethod;
/** The Request Body. */
public content?: ReadableStream;
/** The Query Parameters of the request. */
public queryParameters: Map<string, object> = new Map<string, object>(); //TODO: case insensitive
public queryParameters: Map<string, string | number | boolean | undefined> = new Map<string, string | number | boolean | undefined>(); //TODO: case insensitive
/** The Request Headers. */
public headers: Map<string, string> = new Map<string, string>(); //TODO: case insensitive
private _middlewareOptions = new Map<string, MiddlewareOption>(); //TODO: case insensitive
Expand Down
12 changes: 7 additions & 5 deletions abstractions/typescript/src/store/inMemoryBackingStore.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { BackingStore } from "./backingStore";
import _ from "lodash";
import { v4 as uuidv4 } from 'uuid';

type storeEntryWrapper = {changed: boolean, value: unknown};
Expand Down Expand Up @@ -34,10 +33,13 @@ export class InMemoryBackingStore implements BackingStore {
});
}
public enumerate(): storeEntry[] {
return _.map(this.returnOnlyChangedValues ?
_.filter(this.store, (_, k) => this.store.get(k)?.changed ?? false) :
this.store,
(_, k) => { return { key: k, value: this.store.get(k)?.value}});
let filterableArray = [...this.store.entries()];
if(this.returnOnlyChangedValues) {
filterableArray = filterableArray.filter(([_, v]) => v.changed);
}
return filterableArray.map(([key, value]) => {
return {key, value};
});
}
public enumerateKeysForValuesChangedToNull(): string[] {
const keys: string[] = [];
Expand Down
2 changes: 1 addition & 1 deletion src/Kiota.Builder/CodeDOM/CodeBlock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ returnedValue is CodeProperty cProp &&
// indexer retrofited to method in the parent request builder on the path and conflicting with the collection request builder propeerty
returnedValue = innerChildElements.GetOrAdd($"{element.Name}-indexerbackcompat", element);
added = true;
} else if(currentMethod.IsOfKind(CodeMethodKind.RequestExecutor, CodeMethodKind.RequestGenerator)) {
} else if(currentMethod.IsOfKind(CodeMethodKind.RequestExecutor, CodeMethodKind.RequestGenerator, CodeMethodKind.Constructor)) {
// allows for methods overload
var methodOverloadNameSuffix = currentMethod.Parameters.Any() ? currentMethod.Parameters.Select(x => x.Name).OrderBy(x => x).Aggregate((x, y) => x + y) : "1";
returnedValue = innerChildElements.GetOrAdd($"{element.Name}-{methodOverloadNameSuffix}", element);
Expand Down
5 changes: 4 additions & 1 deletion src/Kiota.Builder/CodeDOM/CodeParameter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ public enum CodeParameterKind
CurrentPath,
Options,
Serializer,
BackingStore
BackingStore,
RawUrl
}

public class CodeParameter : CodeTerminal, ICloneable, IDocumentedElement
Expand All @@ -28,6 +29,7 @@ public CodeParameter(CodeElement parent): base(parent)
public CodeTypeBase Type {get;set;}
public bool Optional {get;set;}= false;
public string Description {get; set;}
public string DefaultValue {get; set;}
public bool IsOfKind(params CodeParameterKind[] kinds) {
return kinds?.Contains(ParameterKind) ?? false;
}
Expand All @@ -39,6 +41,7 @@ public object Clone()
Name = Name.Clone() as string,
Type = Type?.Clone() as CodeTypeBase,
Description = Description?.Clone() as string,
DefaultValue = DefaultValue?.Clone() as string,
};
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/Kiota.Builder/CodeDOM/CodeProperty.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ public enum CodePropertyKind
BackingStore,
PathSegment,
CurrentPath,
HttpCore
HttpCore,
RawUrl
}

public class CodeProperty : CodeTerminal, IDocumentedElement
Expand Down
2 changes: 1 addition & 1 deletion src/Kiota.Builder/CodeDOM/CodeTypeBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ protected CodeTypeBase(CodeElement parent) : base(parent) {
public CodeTypeCollectionKind CollectionKind {get;set;} = CodeTypeCollectionKind.None;
public bool IsCollection { get { return CollectionKind != CodeTypeCollectionKind.None; } }
public bool IsArray { get { return CollectionKind == CodeTypeCollectionKind.Array; } }
public ChildType BaseClone<ChildType>(CodeTypeBase source) where ChildType : CodeTypeBase
protected ChildType BaseClone<ChildType>(CodeTypeBase source) where ChildType : CodeTypeBase
{
ActionOf = source.ActionOf;
IsNullable = source.IsNullable;
Expand Down
Loading