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

WebDAV System Module #21106

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

SHBBusinessSolutions
Copy link

@JesperSchulz
Would you be interested in getting a System Application module for connecting to WebDAV like the Azure Blob Storage or Sharepoint API apps?

We already connected a PTE and do have a bit of experience in that regard. But we would need a bit of structure on how to create the app "in the Microsoft way" and on how to create the test automation.

@pri-kise
Copy link
Contributor

@SHBBusinessSolutions you already mention good examples.
You should make use of the facade pattern and if you want to provide the possiblity to set optional parameters it's also useful to make to the "Parameter Codeunit"-Pattern.

Additionally you should focus on secure authentication patterns and avoid Support for Basic Authentication.

@JulianTillmann
Copy link
Contributor

@pri-kise
thanks for the input

regarding authentication, there's not a lot of options with WebDAV available. We'll take a look a digest authentication and Microsoft should decide if we should support NTLM / kerberos.

But basic auth should be available in my opinion. The usage scenarios that we see include simple 3rd party OnPrem solutions where Basic Auth would be the lowest common denominator.

@JulianTillmann
Copy link
Contributor

@JesperSchulz
is there any interest here or shoud we scrap this idea?

@JesperSchulz
Copy link
Contributor

Generally, it sounds like a very useful module to have! But I worry a little about the request for Microsoft's involvement in this case, as I can't really free up any resources at the moment to provide special support for this pull request.
Do you think you could get through this project with usual support from us and the community?

@JulianTillmann
Copy link
Contributor

Generally, it sounds like a very useful module to have! But I worry a little about the request for Microsoft's involvement in this case, as I can't really free up any resources at the moment to provide special support for this pull request. Do you think you could get through this project with usual support from us and the community?

I'm not sure what the "usual support" would be ;-)

I at least would need the app-manifest(s) prepared from you guys.

And some guidance on what to do with test automation, since it's an external API we're connecting to: Should there be a fixed test-webdav instance somewhere or a powershell script to setup Webdav on a Docker IIS or do we need a complete mock interface?

And maybe an input on what authentication types to support

@pri-kise
Copy link
Contributor

pri-kise commented Dec 8, 2022

@JulianTillmann "usual support" normally involves review of code and after some draft of your pull request, suggestions for refactoring / restructuring.

There are at least a few major principles.
Make use of the FacaPattern (https://alguidelines.dev/docs/patterns/facade-pattern/).
Add Documentation to your public codeunits.

I'm no expert in testing, but I received some feedback for my PR from @adrogin .
You can have a look at the following open PR (#21246), that also adds a new Module:
Simple Copy the app.json to you folder and change the name, the guid and provide some short description.

@JesperSchulz
Copy link
Contributor

We've also got some documentation on how to get started with a new module here: http://aka.ms/newalmodule.
As pri-kise mentions, the usual support is communicating back and forth here on GitHub. We can give tips and tricks and do code reviews, but we cannot "take over" certain parts, such as writing the tests for you (or the likes of it). How about you just give it a shot, create a draft PR, and we'll guide you through it? And if you grant me access to your branch, I can even assist you with small commits if need be 😊 Let's do this!

@JulianTillmann
Copy link
Contributor

@microsoft-github-policy-service agree [company="SHB Business Solutions GmbH"]

@JulianTillmann
Copy link
Contributor

okay, so I made a prototype and tested it manually against IIS Webdav and Nextcloud WebDAV with BasicAuth and leaning heavily on the Sharepoint API system app.

There are basically two parts on which I need your input:

  1. I would provide automated testing if I knew where and how to build the third party components. Would it be acceptable to use the docker IIS and configure a WebDAV instance against which can be tested? Or are the other WebDAV providers that need to be considered?

  2. I only implemented BasicAuth right now. DigestAuth is a bit tricky and there's not much information I can implement easily for BC. What about NTLM authentication? HttpClient supports it OnPrem, does it make sense to implement it in the system application?

Looking forward to your answers

Copy link
Contributor

@PeterConijn PeterConijn left a comment

Choose a reason for hiding this comment

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

This is an exciting idea and it certainly looks like a good start. I did notice a few generic things (I haven't flagged all of them individually):

  • The MIT heading was missing in some of the objects
  • Exit should not be capitalized
  • A lot of superfluous whitespace in several places
  • Missing xml documentation tags for public codeunits

{
Access = Public;

procedure GetWebDAVBasicAuth(Username: Text; Password: Text): Interface "WebDAV Authorization"
Copy link
Contributor

Choose a reason for hiding this comment

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

This pattern kind of defeats the NonDebuggable in the implementation codeunit, since you are passing the credentials as plaintext in the argument. One option to make this more secure is to consider storing the password in the isolated storage and retrieving it from there in the implementation. Would that be a viable option?

Copy link
Contributor

Choose a reason for hiding this comment

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

Same thing is happening at the SharePoint Auth., the client secret is added in plain text.

This is a Basic Auth interface. If I add an isolated storage implementation I force everybody to use this isolated storage implemenation.

Maybe I could force a Base64 encoding of the password? Would that be okay?

Copy link
Contributor

Choose a reason for hiding this comment

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

Base64 is not a good security measure. Perhaps making the procedure in the public facade non-debuggable would be enough, since at that point, the process as far as this implementation goes should be reasonable secure.

Copy link
Contributor

Choose a reason for hiding this comment

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

Base64 isn't a good security measure but that's how Authentication header Basic works, https://www.rfc-editor.org/rfc/rfc7235#section-1.1.

I don't think anyone likes credentials that can be easily seen/retrieved. There is a new datatype that may come in wave 2 release (v23) which securely prevents the values from being seen. But until then, adding NonDebuggable is the way to go. If the username/passwords are being stored in application, I'd recommend to also provide a IsolatedStorage way to set and then auth with them without retrieving it from DB which can lead to insecurely storing them.

codeunit 5680 "WebDAV Basic Authorization"
{
Access = Public;

Copy link
Contributor

Choose a reason for hiding this comment

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

Since this is the public facade, I would add xml comments to the codeunit and the procedure here.

Copy link
Contributor

Choose a reason for hiding this comment

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

done

WebDAVBasicAuthImpl: Codeunit "WebDAV Basic Auth. Impl.";
begin
WebDAVBasicAuthImpl.SetUserNameAndPassword(Username, Password);
Exit(WebDAVBasicAuthImpl);
Copy link
Contributor

Choose a reason for hiding this comment

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

No capitalization is needed for "exit"

Copy link
Contributor

Choose a reason for hiding this comment

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

corrected

end;

[NonDebuggable]
internal procedure SetParameters(NewIsSuccesss: Boolean; NewHttpStatusCode: Integer; NewResponseReasonPhrase: Text; NewRetryAfter: Integer; NewErrorMessage: Text)
Copy link
Contributor

Choose a reason for hiding this comment

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

Codeunit access modifier is already Internal. You can remove the access modifier here.

Copy link
Contributor

Choose a reason for hiding this comment

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

removed

Copy link
Contributor

Choose a reason for hiding this comment

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

I still see the internal modifier


[NonDebuggable]
[TryFunction]
internal procedure GetResultAsText(var Result: Text);
Copy link
Contributor

Choose a reason for hiding this comment

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

I believe the general pattern is to prefix try procedures with the word Try, so TryGetResultAsText.

Also, since the codeunit access modifier is Internal, the access modifiers for the procedures are superfluous.

Copy link
Contributor

Choose a reason for hiding this comment

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

prefix added and NonDEbuggable removed

local procedure GetNextEntryNo(): Integer;
begin
NextEntryNo += 1;
Exit(NextEntryNo);
Copy link
Contributor

Choose a reason for hiding this comment

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

exit

Copy link
Contributor

Choose a reason for hiding this comment

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

corrected


local procedure GetLevelFromPath(Path: Text): Integer
begin
Exit(Path.Split('/').Count);
Copy link
Contributor

Choose a reason for hiding this comment

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

exit

Copy link
Contributor

Choose a reason for hiding this comment

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

corrected

@@ -0,0 +1,84 @@
codeunit 5678 "WebDAV Client"
Copy link
Contributor

Choose a reason for hiding this comment

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

This is the facade, so xml documentation would need to be added here. (Also MIT stuff)

Copy link
Contributor

Choose a reason for hiding this comment

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

XML documentation and MIT headers added

WebDAVClientImpl.Initialize(BaseUrl, WebDAVAuthorization);
end;

// [NonDebuggable]
Copy link
Contributor

Choose a reason for hiding this comment

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

Should all these be here or not?

Copy link
Contributor

Choose a reason for hiding this comment

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

removed NonDebuggable

Copy link
Contributor

Choose a reason for hiding this comment

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

I see that the NonDebuggable are commented out, remove them entirely if not needed.

Access = Internal;

var

Copy link
Contributor

Choose a reason for hiding this comment

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

Remove whitespace

Copy link
Contributor

Choose a reason for hiding this comment

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

done

@JulianTillmann
Copy link
Contributor

JulianTillmann commented Jan 30, 2023

@PeterConijn
thanks for your feedback, I will clean up the code and go over the documentation once there is a clear idea of what to implement.

but I'm still not getting answers to my two fundemental questions:

  1. what kind of authentication do we need to implement?
  2. how would automated testing work with an external webdav component

@PeterConijn
Copy link
Contributor

PeterConijn commented Jan 30, 2023

To start with your second question: you cannot test an actual web call, since it requires external connection. What I often do when testing such functionality is two things:

  1. Test the creation of the http call
    Basically, create GET, POST, PUT, etc calls and see if the HttpClient is structured correctly. This also entails expected error handling (e.g. what should happen if a url is blank, or credentials are missing and the authentication is not set to Anonymous)

  2. Create some mock responses and feed them into your response handler. This would also entail successful responses (200), but also what is expected to happen when common errors occur, such as 401, 403, 404, 500.

As far as authentication goes: that might bear a bit more thought. Basic is still being used a lot, but OAuth 2.0 is gaining traction quickly. Perhaps - and this is just an idea - we can redesign the authentication to use an interface structure, so that we can provide some methods, but it is extensible if someone needs a more exotic method.

I will be away for a couple of weeks and not be able to re-review until after, so I hope you'll forgive my lack of response after this one :)

I missed it in the CR, but given the nature of the funtionality, your test app will likely need access to your module's internals, so you may need to add the internalsVisibleTo flag in your app.json.

@JulianTillmann
Copy link
Contributor

@PeterConijn
regarding tests:
so no real WebDAV endpoint is required for this testing? For Azure Blob Storage there is a Azurite instance being created as far as I understood.

mocking up a xml response seems time consuming and I would there's a big chance I would accidentaly create this mock up so that the test works. A real scenario would be better, but if that's okay with you than I'll do the mock testing

regarding authentication
Oauth2 is not supported with webdav as far as I know. I only found "Basic" and "Digest" authentication in the WebDAV specs:
http://www.webdav.org/specs/rfc2617.html
IIS is also giving us NTLM / kerberos which is still supported in the HttpClient but only "OnPrem", so I don't know if we should implement this.

But I'll put this in the backlog until you're back and concentrate on the testing for now

@Peter-Conijn
Copy link

From my personal account, but in addition to my comment on testing APIs, you might find this video by @lvanvugt handy: https://www.youtube.com/watch?v=P-8MOfNppVQ

@pri-kise
Copy link
Contributor

@JulianTillmann you can have a look at the sharepoint test codeunit.
https://github.com/microsoft/ALAppExtensions/blob/main/Modules/System%20Tests/SharePoint/src/SharePointClientTest.Codeunit.al

It makes use of manual event subscribers to mock the requests.

@pri-kise
Copy link
Contributor

@JesperSchulz can maybe someone of microsoft have a look at the pull request, too.
The basic requirements were met, by adding test and providing documentation for public procedures..

@JulianTillmann could maybe provide some example could how to call the public procedure and how to initialize the authentication?
You can check out the following example of the sharepoint authorization Module
https://github.com/microsoft/ALAppExtensions/tree/main/Modules/System/SharePoint%20Authorization

'COPY':
GetSuccessfullResponse(Uri, WebDAVOperationResponse);
'MOVE':
GetSuccessfullResponse(Uri, WebDAVOperationResponse);
Copy link
Contributor

Choose a reason for hiding this comment

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

Teeny thing, but Successful is with one l

end;

[NonDebuggable]
procedure Propfind(Uri: Text; Recursive: Boolean) WebDAVOperationResponse: Codeunit "WebDAV Operation Response";
Copy link
Contributor

Choose a reason for hiding this comment

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

Fair point.

{
Caption = 'Level';
}
field(20; "Is Collection"; Boolean)
Copy link
Contributor

Choose a reason for hiding this comment

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

I agree that it happens with older NAV objects, but not sure that is a reason to continue the practice. I will defer to Microsoft's opinion on the matter though.

{
Caption = 'Content Length';
}
field(30; "Creation Date"; DateTime)
Copy link
Contributor

Choose a reason for hiding this comment

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

Understood.

@JesperSchulz
Copy link
Contributor

Indeed looks like this PR now is in a shape, which requires us to look into it. We've implemented a new guideline to not start reviewing, before two partners have approved a PR. Hopefully that will allow things to scale better :-)

@JulianTillmann
Copy link
Contributor

@JesperSchulz
so how will we proceed? Are there any chances of getting this with the next m,ajor update?

I still have the questions about authentication ...

@JesperSchulz
Copy link
Contributor

JesperSchulz commented Feb 28, 2023

The next major is more or less locked down (for new features). This would be a candidate for a minor, following the next major. I'll see if we can find someone to drive this forward, when the 2023 Wave 1 is done and dusted. Should be soon. Then we'll take care of all open questions too.

/// <param name="Username">Text.</param>
/// <param name="Password">Text.</param>
/// <returns>Return value of type Interface "WebDAV Authorization".</returns>
procedure GetWebDAVBasicAuth(Username: Text; Password: Text): Interface "WebDAV Authorization"
Copy link
Contributor

Choose a reason for hiding this comment

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

Add [NonDebuggable]

{
Access = Public;

procedure GetWebDAVBasicAuth(Username: Text; Password: Text): Interface "WebDAV Authorization"
Copy link
Contributor

Choose a reason for hiding this comment

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

Base64 isn't a good security measure but that's how Authentication header Basic works, https://www.rfc-editor.org/rfc/rfc7235#section-1.1.

I don't think anyone likes credentials that can be easily seen/retrieved. There is a new datatype that may come in wave 2 release (v23) which securely prevents the values from being seen. But until then, adding NonDebuggable is the way to go. If the username/passwords are being stored in application, I'd recommend to also provide a IsolatedStorage way to set and then auth with them without retrieving it from DB which can lead to insecurely storing them.

end;

[NonDebuggable]
internal procedure SetParameters(NewIsSuccesss: Boolean; NewHttpStatusCode: Integer; NewResponseReasonPhrase: Text; NewRetryAfter: Integer; NewErrorMessage: Text)
Copy link
Contributor

Choose a reason for hiding this comment

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

I still see the internal modifier

RequestMessage := PrepareRequestMsg('PROPFIND', Uri, WebDAVHttpContent);
RequestMessage.GetHeaders(Headers);

// TODO Depth = 0?
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this something the developers/admin etc should be able to set?

HttpContent := WebDAVHttpContent.GetContent();
HttpContent.GetHeaders(Headers);

if Headers.Contains(GetContentType) then
Copy link
Contributor

Choose a reason for hiding this comment

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

Add () to the end of function calls.
All the calls to GetContentType etc

{
Caption = 'Level';
}
field(20; "Is Collection"; Boolean)
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't have a strong opinion on this as long as it makes sense. Internally, unless it's a localized (US, DK etc) field, we've been going sequentially for new tables in general.
I do notice it jumps from 20 to 22. Any reason 21 is missing?

WebDAVClientImpl.Initialize(BaseUrl, WebDAVAuthorization);
end;

// [NonDebuggable]
Copy link
Contributor

Choose a reason for hiding this comment

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

I see that the NonDebuggable are commented out, remove them entirely if not needed.

@JulianTillmann
Copy link
Contributor

@darjoo
thanks for your input, I'll take a look as soon as I get around to it.

But there is still the big question open if we need to provide more than BasicAuth for WebDAV? There is NTLM available, but that's an OnPrem thing and there is a rather complicated "DigestAuth" that's possible with WebDAV.

@darjoo
Copy link
Contributor

darjoo commented Mar 1, 2023

@JulianTillmann Do we know how widely used WebDAV would be used with onprem customers? I'm not sure if that'd be worth the effort at the beginning unless we know there's demand for that with the OnPrem customers.

I did take a brief look at DigestAuth, and initially I was going to recommend to implement it as well. Maybe you could take a look to see if you could estimate how much effort that would take? It did look complicated as you mentioned. I'm sure all customers will always prefer secure authentication methods, if we could add it.

@JulianTillmann
Copy link
Contributor

@darjoo
We looked at WebDAV as a way to be "Universal Code"-compliant and still get a connection to the OnPrem file servers. You can easily install WebDAV components on the IIS and create fileshares. But BasicAuth is still using the AD or local Windows users, so native NTLM support is not that important in my opinion.

I'll take another look at DigestAuthentication when I get to it. Maybe we could ship this WebDAV module with BasicAuth and deliver Digest later?

@darjoo
Copy link
Contributor

darjoo commented Mar 1, 2023

@JulianTillmann Yeah, that's also fine to deliver DigestAuth later.

Headers.Remove('Authorization');
Headers.Add('Authorization', AuthorizationString);

Headers.Remove('Translate');
Copy link
Contributor

Choose a reason for hiding this comment

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

Minor: This doesn't seem to be related to Authorization? Could either be moved to a different function or Rename existing function to explain what happens. I believe splitting would be best.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't understand what you mean?

Copy link

Choose a reason for hiding this comment

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

I think he is talking about the "Translate" header that is added with the value "F".
Why is this done? Is it related to authorization?

Headers.Remove('Translate');
Headers.Add('Translate', 'F');
end;

Copy link
Contributor

Choose a reason for hiding this comment

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

Minor, double newline

{
Access = Public;

/// <summary>
Copy link
Contributor

Choose a reason for hiding this comment

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

Docs should explain what the function does, what the parameters and return value mean, not the type that can be seen from the procedure. Ex. (I did not spend a lot of time on this so feel free to improve) Summary: "Creates WebDAV Authorization initialized with the given username and password.", Username: "The name of the user that is authenticating the call", same for password, return: "A WebDAV Authorization interface initialized with the credentials".

SuccessStatusCode: Boolean;

/// <summary>
/// Gets reponse details.
Copy link
Contributor

Choose a reason for hiding this comment

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

Same summary all along, also please make return value more human readable.

end;

// TODO DIGEST??
procedure SetRequestDigest(RequestDigestValue: Text)
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't see any use of RequestDigest... is this needed?

// ------------------------------------------------------------------------------------------------
codeunit 5682 "WebDAV Content Parser"
{

Copy link
Contributor

Choose a reason for hiding this comment

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

empty line


if OnlyCollections then
if not WebDAVContent."Is Collection" then
exit;
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it makes sense to return whether the single was parsed or not. In this case exit(false)

exit(XmlNode.SelectSingleNode(PropertyName, XmlNamespaceManager, ChildNode));
end;

local procedure GetNextEntryNo(): Integer;
Copy link
Contributor

Choose a reason for hiding this comment

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

This assumes all entries are created within the context of the same codeunit. Would be better if we in the caller just had a FindLast() initially, then init() and then increase the entry number by 1 if it doesn't already auto-increment.

exit(true);
end;

procedure GetFilesAndCollections(var WebDAVContent: Record "WebDAV Content"; Recursive: Boolean): Boolean
Copy link
Contributor

Choose a reason for hiding this comment

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

All the "WebDAV Content Parser" functions are NonDebuggable, such as the Initialize, if that shouldn't be debuggable, then these functions shouldn't be debuggable either.
Personally I don't see any reason why the Initialize function is NonDebuggable though, so maybe that one should have the tag removed.

end;

[TryFunction]
procedure TryGetResponseAsText(var Response: Text)
Copy link
Contributor

Choose a reason for hiding this comment

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

Again these two functions should just return the response directly instead of using a by-var variable.

@JesperSchulz
Copy link
Contributor

Hi @JulianTillmann,

I'm trying to push all of the open pull requests into the product - and this one has been around for a while 😊 I can see there are quite a few unaddressed code review comments; some which must be looked at before we can proceed with this PR.

When do you think you'll find the time wrap this module up? We're not in a hurry as such, but ideally I'd love to get this one merged before its 1 year anniversary 😋

Let me know how I can help get this wrapped up! Looking forward to hear back from you!

@JulianTillmann
Copy link
Contributor

@JesperSchulz
I don't see this happening soon. Since the support on other parts of the product is de facto non existant, we have to put our resources elsewhere right now.

also this has not been the best experience contributing, lot's of "style" feedback but questions about content not beeing answered. And it doesn't seem like anybody really test it in a real scenario, only mock tests are expected.

@JesperSchulz
Copy link
Contributor

JesperSchulz commented Aug 18, 2023

Sorry to hear that, Julian. All of it.
The one thing I can do something about, is the experience. I understand your feedback and will take it with me, when we plan for the next iteration of our open source story. The "process" in this first attempt to allow for contributions has indeed had its flaws - for one, it was hardly existing. We were "trying things out", so to speak. That has already changed in the BaseApp contribution pilot, and will be enforced to a whole new level when the System Application soon goes fully open source. I fully agree, that the level of collaboration must improve. And it will.
I will leave this PR open for now, as I still do have hope, that we will find the time to finish it up at some point. We're dealing with the same problems as you though - it's not always easy to find the time to do all the things we would love to do.
Anyway, thanks for giving this a shot and I hope we'll be in touch!

@JesperSchulz JesperSchulz added the system application This PR is related to the system application and must be completed asap (migration in progress) label Nov 7, 2023
@JesperSchulz JesperSchulz added the Integration GitHub request for Integration area label Apr 22, 2024
@aholstrup1 aholstrup1 requested a review from a team as a code owner September 9, 2024 11:29
@JesperSchulz JesperSchulz self-assigned this Dec 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Integration GitHub request for Integration area system application This PR is related to the system application and must be completed asap (migration in progress)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants