Skip to content

Commit

Permalink
Merge pull request #508 from microsoft/feature/raw-url
Browse files Browse the repository at this point in the history
- adds support for raw URLs in request builders
  • Loading branch information
baywet authored Aug 25, 2021
2 parents 05ffc45 + ada8b75 commit 443f1d8
Show file tree
Hide file tree
Showing 43 changed files with 565 additions and 184 deletions.
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]] = ""
}
}
}
} 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

0 comments on commit 443f1d8

Please sign in to comment.