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

Any interest in extending the MessageCard type? #6

Closed
atc0005 opened this issue Mar 26, 2020 · 12 comments
Closed

Any interest in extending the MessageCard type? #6

atc0005 opened this issue Mar 26, 2020 · 12 comments
Labels
enhancement New feature or request
Milestone

Comments

@atc0005
Copy link
Collaborator

atc0005 commented Mar 26, 2020

Overview

I'm working on several small projects where I'm looking to use this package for submitting notifications to Microsoft Teams. As part of that work, I plan on including JSON data and some verbose client details.

To that end I'm looking at using sections with multiple facts entries, but I'm not sure yet of the final payload details.

Example JSON

Example, stripped down copy of the JSON content that I'm toying with:

{
    "@type": "MessageCard",
    "@context": "http://schema.org/extensions",
    "themeColor": "0076D7",
    "summary": "Summary text goes here",
    "text": "Text example",
    "sections": [
        {
            "facts": [
                {
                    "name": "Assigned to",
                    "value": "Unassigned"
                },
                {
                    "name": "Due date",
                    "value": "Mon May 01 2017 17:07:18 GMT-0700 (Pacific Daylight Time)"
                },
                {
                    "name": "Status",
                    "value": "Not started"
                }
            ],
            "markdown": true
        }
    ]
}

Required fields

FWIW, the reference guide says that these two fields are required, but from testing it appears that Teams is pretty lenient about leaving them out:

{
    "@type": "MessageCard",
    "@context": "https://schema.org/extensions"
}

Draft 1 changes

type MessageCard struct {
	// Required; must be set to "MessageCard"
	Type string `json:"@type"`
	// Required; must be set to "https://schema.org/extensions"
	Context string `json:"@context"`

	// Summary appears to only be used when there are sections defined
	Summary    string `json:"summary,omitempty"`
	Title      string `json:"title"`
	Text       string `json:"text"`
	ThemeColor string `json:"themeColor,omitempty"`
	Sections   []struct {
		Title    string `json:"title"`
		Text     string `json:"text"`
		Markdown bool   `json:"markdown"`
		Facts    []struct {
			Name  string `json:"name"`
			Value string `json:"value"`
		} `json:"facts,omitempty"`
	} `json:"sections,omitempty"`
	PotentialAction []struct {
		Target          []string    `json:"target"`
		Context         string      `json:"@context"`
		Type            string      `json:"@type"`
		ID              interface{} `json:"@id"`
		Name            string      `json:"name"`
		IsPrimaryAction bool        `json:"isPrimaryAction"`
	} `json:"potentialAction,omitempty"`
}

The struct changes were generated by running the sample JSON through https://mholt.github.io/json-to-go/ and then tweaking the output to add additional omitifempty qualifiers. I have only just begun testing using the changes, so I can't speak to the accuracy of the results.

// NewMessageCard - create new empty message card
func NewMessageCard() MessageCard {

	// define expected values to meet Office 365 Connector card requirements
	// https://docs.microsoft.com/en-us/outlook/actionable-messages/message-card-reference#card-fields
	msgCard := MessageCard{
		Type:    "MessageCard",
		Context: "https://schema.org/extensions",
	}

	return msgCard
}

I don't know if this is the right way to do it, but I modified the constructor function to handle presetting the required fields since they're static values. While Teams is happy to accept payloads without those fields, since their docs note they're required it seems like good future-proofing to go ahead and include them.

Draft 2 changes

type MessageCardSectionFact struct {
	Name  string `json:"name"`
	Value string `json:"value"`
}

type MessageCardSection struct {
	Title      string                   `json:"title,omitempty"`
	Text       string                   `json:"text,omitempty"`
	Markdown   bool                     `json:"markdown,omitempty"`
	Facts      []MessageCardSectionFact `json:"facts,omitempty"`
	StartGroup bool                     `json:"startGroup,omitempty"`
}

type MessageCard struct {
	Type       string               `json:"@type"`
	Context    string               `json:"@context"`
	Summary    string               `json:"summary,omitempty"`
	Title      string               `json:"title,omitempty"`
	Text       string               `json:"text"`
	ThemeColor string               `json:"themeColor,omitempty"`
	Sections   []MessageCardSection `json:"sections,omitempty"`
}

Stripped out all comments in the above to save space. There may be other fields to add/remove to suit more general needs. No changes to the NewMessageCard() constructor since Draft 1.

References

@dasrick dasrick added this to the v2 milestone Mar 26, 2020
@dasrick dasrick added the enhancement New feature or request label Mar 26, 2020
@dasrick
Copy link
Owner

dasrick commented Mar 29, 2020

@atc0005 I tried to compare draft 1 and 2. some thoughts:

  • draft 2 is much better structured/readable
  • is there a reason why PotentialAction is not part of draft 2
  • for me it looks compatible with the current implementation

Would you like to start with a branch? (It looks better for me when you are the contributor ...)

@atc0005
Copy link
Collaborator Author

atc0005 commented Mar 29, 2020

@dasrick: I tried to compare draft 1 and 2. some thoughts:

  • draft 2 is much better structured/readable

Thanks, I was hoping so. The first draft was nearly 1:1 from the output generated by https://mholt.github.io/json-to-go/, which was a great starting point (IMHO) since I'm not yet familiar enough to craft a Go struct equivalent from JSON objects. I also tried to trim back fields that didn't appear strictly necessary with the thoughts that they could be added in a later revision if needed.

  • is there a reason why PotentialAction is not part of draft 2

Mostly scope. Before I checked with you to see if you had interest in incorporating the changes here, I decided I would press forward either way with implementing changes that I need for another project (which uses this package).

I knew that my Go skills are still very limited (I consider myself still very new to the language), so I didn't want to try and implement too much, too early if I couldn't find away to do it properly.

  • for me it looks compatible with the current implementation

From my initial, limited testing I came to the same conclusion. The additional fields allowed for further expansion of functionality without breaking any existing code that only used the initial three fields.

@atc0005
Copy link
Collaborator Author

atc0005 commented Mar 29, 2020

@dasrick: Would you like to start with a branch? (It looks better for me when you are the contributor ...)

I can. How quickly are you looking to move forward with this? I've started hacking together some large changes to test a few ideas on my fork and was planning on cleaning things up before presenting here.

A few changes from the fork worth sharing now:

  • Methods to create sections, facts and add facts to said sections

Example methods (and just signature for the last one):

func (mc *MessageCard) AddSection(section ...MessageCardSection) {
	mc.Sections = append(mc.Sections, section...)
}

// AddFact adds one or many additional MessageCardSectionFact values to a
// MessageCardSection
func (mcs *MessageCardSection) AddFact(fact ...MessageCardSectionFact) {
	mcs.Facts = append(mcs.Facts, fact...)
}

// AddFactFromKeyValue accepts a key and slice of values and converts them to
// MessageCardSectionFact values
func (mcs *MessageCardSection) AddFactFromKeyValue(key string, values ...string) error {
...
}
  • Functions to convert strings to Markdown code snippets or code blocks

    • intended to allow formatting fact key/value (where formatting would be applied to value) pairs
    • intended to allow formatting text fields in sections
      • e.g., for providing JSON received via API endpoints for review
  • Package level logger (as noted on What are your thoughts on a package-level logger that is disabled by default? #10)

  • Expanding error messages from message submission attempts to include the response body text

    • e.g., this proved useful when submitting messages missing both text and summary fields as the API returned "400 Bad Request" as the status code and "Summary or Text is required." as the response body
  • Constructor function for new message card section

  • Export isValidWebhookURL

  • Add new exported IsValidMessageCard validation function to check for a combination of missing text and summary fields

    • this is a known failure condition, so it seemed like a good idea to cover the scenario and generate an error message from the library before the remote API is forced to reject with an error

This may be some other changes, but this is what I spotted when doing a quick review to gather notes for posting here.

@atc0005
Copy link
Collaborator Author

atc0005 commented Mar 29, 2020

In case my uses cases are too vague, here is an example of the output generated by a small webhook payload testing app:

https://github.com/atc0005/bounce#local-submit-json-payload-using-curl-to-json-specific-endpoint-get-formatted-response

and here is a screenshot showing the received message generated by a small CLI app I wrote (and am heavily refactoring) which uses a fork of this project:

teams-multiple-sections-with-formatted-text-and-fact-values-2020-03-29

That fork contains the changes I mentioned in my last post. I'm hoping to cleanup those changes and break into specific change sets. I'd then use those sets with Pull Requests for potential inclusion here.

@atc0005
Copy link
Collaborator Author

atc0005 commented Mar 29, 2020

Back to your notes here:

  • is there a reason why PotentialAction is not part of draft 2
  • for me it looks compatible with the current implementation

If you want, I can spin off a branch to include the type changes noted in the OP (going with a draft 3 that brings back PotentialAction) and we can discuss each of the other items separately (or in groups of changes that make sense / seem acceptable).

Which branch should I file a Pull Request against? I assume v2, but just want to be sure. I don't yet have a full understanding of the v2 Go modules setup; I've read the docs, but have only glossed over them as I'm not ready for even a v1 across my own projects.

@atc0005
Copy link
Collaborator Author

atc0005 commented Mar 29, 2020

  • Methods to create sections, facts and add facts to said sections

Follow-up: I still feel that the idea fits, but the current implementation has a bug (just in case you look at them) that I'm working through.


EDIT: It was a bug elsewhere, the methods appear to work as intended.

@atc0005
Copy link
Collaborator Author

atc0005 commented Mar 30, 2020

Worth noting (scratch notes from this morning's pre-work dev time):

The PotentialAction field that was removed in Draft 2 can be defined on both Sections and the main message card itself. The reference docs also mention 5 different types as supported, but only list 4. The example JSON file I used for testing via curl had a ViewAction type set on it which is not listed in the 4 types in the reference doc. Perhaps it is deprecated.

@atc0005
Copy link
Collaborator Author

atc0005 commented Mar 31, 2020

The more I dig into this, the more I keep returning to the idea that support for PotentialAction should be added as a further enhancement and not as an initial "feature" when extending the MessageCard type.

https://docs.microsoft.com/en-us/outlook/actionable-messages/message-card-reference#actions

As noted previously, 5 different types are supported (with one of them presumably deprecated since it's not listed) and each type of potentialAction entry has different sets of fields (some common, some not). This suggests that we'll need different Go types/structs for each of the possible potentialAction types along with constructors to create them with required fields predefined (presumably there are required fields for these types, but I've not dug far enough into it yet).

@atc0005
Copy link
Collaborator Author

atc0005 commented Mar 31, 2020

The PotentialAction field that was removed in Draft 2 can be defined on both Sections and the main message card itself. The reference docs also mention 5 different types as supported, but only list 4. The example JSON file I used for testing via curl had a ViewAction type set on it which is not listed in the 4 types in the reference doc. Perhaps it is deprecated.

It is indeed deprecated:

Here are a couple of suggestions:

  • You should include the summary property which is required, even though the documentation doesn't say it. We'll fix that.
  • Try replacing your ViewAction with an OpenUri action. ViewAction is deprecated. Although ViewAction is still supported, we want to encourage everyone to use OpenUri instead.

refs https://stackoverflow.com/a/44061405


EDIT: Also mentioned here:

https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/connectors-using

(A fourth action, ViewAction, is still supported but no longer needed; use OpenUri instead.)

That note is relevant, but potentially confusing though, as there are five types with only four being relevant to Microsoft Teams:

  1. ViewAction
  2. OpenUri
  3. HttpPOST
  4. ActionCard
  5. InvokeAddInCommand
    • Outlook specific based on what I read

@dasrick
Copy link
Owner

dasrick commented Apr 2, 2020

Which branch should I file a Pull Request against? I assume v2, but just want to be sure. I don't yet have a full understanding of the v2 Go modules setup; I've read the docs, but have only glossed over them as I'm not ready for even a v1 across my own projects.

The branch "v2" will be removed soon. The branch master represent now "v2..". This should be the branch to setup a pull request against.

The more I dig into this, the more I keep returning to the idea that support for PotentialAction should be added as a further enhancement and not as an initial "feature" when extending the MessageCard type.

Agree. It's always a better idea to keep the changes small.
So, i would prefer to start without PotentialAction. We will include it in a further release.

@atc0005
Copy link
Collaborator Author

atc0005 commented Apr 2, 2020

The branch "v2" will be removed soon. The branch master represent now "v2..". This should be the branch to setup a pull request against.

Acknowledged. I'll try to get something together over the next day or so.

As mentioned previously I've made a number of changes as part of testing integration with a few projects of mine. How small would you like the Pull Requests?

I can go with just the type changes or type changes + associated methods.

There are other changes that I'm not even sure are a solid fit, so I wouldn't want to include those just yet, though they would likely show up in a draft PR not long behind to aid in further discussions.

Agree. It's always a better idea to keep the changes small.
So, i would prefer to start without PotentialAction. We will include it in a further release.

Sounds good to me. I fought with it for a while before I realized I wasn't ready yet to take it on. I'm still giving it some thought, so maybe after the other changes land and stabilize we can look further into it.

atc0005 added a commit to atc0005/go-teams-notify that referenced this issue Apr 4, 2020
NEW:

- `MessageCard` type includes additional fields
  - `Type` and `Context` fields provide required JSON payload
    fields
    - preset to required static values via updated
      `NewMessageCard()` constructor
  - `Summary`
    - required if `Text` field is not set, optional otherwise
  - `Sections` slice
    - `MessageCardSection` type

- Additional nested types
  - `MessageCardSection`
  - `MessageCardSectionFact`
  - `MessageCardSectionImage`

- Additional methods for `MessageCard` and nested types
  - `MessageCard.AddSection()`
  - `MessageCardSection.AddFact()`
  - `MessageCardSection.AddFactFromKeyValue()`
  - `MessageCardSection.AddImage()`
  - `MessageCardSection.AddHeroImageStr()`
  - `MessageCardSection.AddHeroImage()`

- Additional factory functions
  - `NewMessageCardSection()`
  - `NewMessageCardSectionFact()`
  - `NewMessageCardSectionImage()`

- `IsValidMessageCard` added to check for minimum required
   field values.
   - This function has the potential to be extended
     later with additional validation steps.

CHANGED:

- `MessageCard` type includes additional fields
- `NewMessageCard` factory function sets fields needed for
   required JSON payload fields
  - `Type`
  - `Context`

- `teamsClient.Send()` method updated to apply `MessageCard` struct
  validation alongside existing webhook URL validation

- `isValidWebhookURL` exported as `IsValidWebhookURL` so that client
  code can use the validation functionality instead of repeating the
  code
    - e.g., flag value validation for "fail early" behavior

KNOWN ISSUES:

- No support in this set of changes for `potentialAction` types
  - `ViewAction`
  - `OpenUri`
  - `HttpPOST`
  - `ActionCard`
  - `InvokeAddInCommand`
    - Outlook specific based on what I read; likely not included
      in a future release due to non-Teams specific usage

refs dasrick#6
atc0005 added a commit to atc0005/go-teams-notify that referenced this issue Apr 4, 2020
NEW:

- `MessageCard` type includes additional fields
  - `Type` and `Context` fields provide required JSON payload
    fields
    - preset to required static values via updated
      `NewMessageCard()` constructor
  - `Summary`
    - required if `Text` field is not set, optional otherwise
  - `Sections` slice
    - `MessageCardSection` type

- Additional nested types
  - `MessageCardSection`
  - `MessageCardSectionFact`
  - `MessageCardSectionImage`

- Additional methods for `MessageCard` and nested types
  - `MessageCard.AddSection()`
  - `MessageCardSection.AddFact()`
  - `MessageCardSection.AddFactFromKeyValue()`
  - `MessageCardSection.AddImage()`
  - `MessageCardSection.AddHeroImageStr()`
  - `MessageCardSection.AddHeroImage()`

- Additional factory functions
  - `NewMessageCardSection()`
  - `NewMessageCardSectionFact()`
  - `NewMessageCardSectionImage()`

- `IsValidMessageCard()` added to check for minimum required
   field values.
   - This function has the potential to be extended
     later with additional validation steps.

CHANGED:

- `MessageCard` type includes additional fields
- `NewMessageCard` factory function sets fields needed for
   required JSON payload fields
  - `Type`
  - `Context`

- `teamsClient.Send()` method updated to apply `MessageCard` struct
  validation alongside existing webhook URL validation

- `isValidWebhookURL()` exported as `IsValidWebhookURL()` so that client
  code can use the validation functionality instead of repeating the
  code
    - e.g., flag value validation for "fail early" behavior

KNOWN ISSUES:

- No support in this set of changes for `potentialAction` types
  - `ViewAction`
  - `OpenUri`
  - `HttpPOST`
  - `ActionCard`
  - `InvokeAddInCommand`
    - Outlook specific based on what I read; likely not included
      in a future release due to non-Teams specific usage

refs dasrick#6
atc0005 added a commit to atc0005/go-teams-notify that referenced this issue Apr 5, 2020
NEW:

- `MessageCard` type includes additional fields
  - `Type` and `Context` fields provide required JSON payload
    fields
    - preset to required static values via updated
      `NewMessageCard()` constructor
  - `Summary`
    - required if `Text` field is not set, optional otherwise
  - `Sections` slice
    - `MessageCardSection` type

- Additional nested types
  - `MessageCardSection`
  - `MessageCardSectionFact`
  - `MessageCardSectionImage`

- Additional methods for `MessageCard` and nested types
  - `MessageCard.AddSection()`
  - `MessageCardSection.AddFact()`
  - `MessageCardSection.AddFactFromKeyValue()`
  - `MessageCardSection.AddImage()`
  - `MessageCardSection.AddHeroImageStr()`
  - `MessageCardSection.AddHeroImage()`

- Additional factory functions
  - `NewMessageCardSection()`
  - `NewMessageCardSectionFact()`
  - `NewMessageCardSectionImage()`

- `IsValidMessageCard()` added to check for minimum required
   field values.
   - This function has the potential to be extended
     later with additional validation steps.

CHANGED:

- `MessageCard` type includes additional fields
- `NewMessageCard` factory function sets fields needed for
   required JSON payload fields
  - `Type`
  - `Context`

- `teamsClient.Send()` method updated to apply `MessageCard` struct
  validation alongside existing webhook URL validation

- `isValidWebhookURL()` exported as `IsValidWebhookURL()` so that client
  code can use the validation functionality instead of repeating the
  code
    - e.g., flag value validation for "fail early" behavior

KNOWN ISSUES:

- No support in this set of changes for `potentialAction` types
  - `ViewAction`
  - `OpenUri`
  - `HttpPOST`
  - `ActionCard`
  - `InvokeAddInCommand`
    - Outlook specific based on what I read; likely not included
      in a future release due to non-Teams specific usage

refs dasrick#6
atc0005 added a commit to atc0005/go-teams-notify that referenced this issue Apr 7, 2020
NEW:

- `MessageCard` type includes additional fields
  - `Type` and `Context` fields provide required JSON payload
    fields
    - preset to required static values via updated
      `NewMessageCard()` constructor
  - `Summary`
    - required if `Text` field is not set, optional otherwise
  - `Sections` slice
    - `MessageCardSection` type

- Additional nested types
  - `MessageCardSection`
  - `MessageCardSectionFact`
  - `MessageCardSectionImage`

- Additional methods for `MessageCard` and nested types
  - `MessageCard.AddSection()`
  - `MessageCardSection.AddFact()`
  - `MessageCardSection.AddFactFromKeyValue()`
  - `MessageCardSection.AddImage()`
  - `MessageCardSection.AddHeroImageStr()`
  - `MessageCardSection.AddHeroImage()`

- Additional factory functions
  - `NewMessageCardSection()`
  - `NewMessageCardSectionFact()`
  - `NewMessageCardSectionImage()`

- `IsValidMessageCard()` added to check for minimum required
   field values.
   - This function has the potential to be extended
     later with additional validation steps.

- Wrapper `IsValidInput()` added to handle all validation
  needs from one location.
  - the intent was to both solve a CI erro and provide
    a location to easily extend validation checks in
    the future (if needed)

CHANGED:

- `MessageCard` type includes additional fields
- `NewMessageCard` factory function sets fields needed for
   required JSON payload fields
  - `Type`
  - `Context`

- `teamsClient.Send()` method updated to apply `MessageCard` struct
  validation alongside existing webhook URL validation

- `isValidWebhookURL()` exported as `IsValidWebhookURL()` so that client
  code can use the validation functionality instead of repeating the
  code
    - e.g., flag value validation for "fail early" behavior

KNOWN ISSUES:

- No support in this set of changes for `potentialAction` types
  - `ViewAction`
  - `OpenUri`
  - `HttpPOST`
  - `ActionCard`
  - `InvokeAddInCommand`
    - Outlook specific based on what I read; likely not included
      in a future release due to non-Teams specific usage

refs dasrick#6
@atc0005
Copy link
Collaborator Author

atc0005 commented Apr 8, 2020

I added some review comments on #12; noting that here in case those remarks get lost in other repo notification traffic.

On that PR I asked about the scopelint linter warning that my changes generated. I'll go ahead and submit another PR (referencing this issue) to make it easier to discuss that specifically.

atc0005 referenced this issue in atc0005/go-teams-notify Apr 8, 2020
Error from `scopelint` linter:

"Using a reference for the variable on range scope `img`"

Fix:

Use index from range loop to *reach into* the slice argument
to retrieve the address of those values instead of using the
range loop variable, which likely would have been a subtle
source of bugs later on.

refs #6, #11, #12
atc0005 referenced this issue in atc0005/go-teams-notify Apr 8, 2020
Error from `scopelint` linter:

"Using a reference for the variable on range scope `img`"

Fix:

Use index from range loop to *reach into* the slice argument
to retrieve the address of those values instead of using the
range loop variable, which likely would have been a subtle
source of bugs later on.

refs #6, #11, #12
atc0005 referenced this issue in atc0005/go-teams-notify Apr 9, 2020
Error from `scopelint` linter:

"Using a reference for the variable on range scope `img`"

Fix:

Use index from range loop to *reach into* the slice argument
to retrieve the address of those values instead of using the
range loop variable, which likely would have been a subtle
source of bugs later on.

refs #6, #11, #12
atc0005 referenced this issue in atc0005/go-teams-notify Apr 9, 2020
Error from `scopelint` linter:

"Using a reference for the variable on range scope `img`"

Fix:

Use index from range loop to *reach into* the slice argument
to retrieve the address of those values instead of using the
range loop variable, which likely would have been a subtle
source of bugs later on.

refs #6, #11, #12
atc0005 referenced this issue in atc0005/go-teams-notify Apr 9, 2020
Extend error handling by retrieving and providing the
response string from the remote API as part of the error
message returned to the caller if the status code
indicates an error condition.

Based on testing, the response string contains useful
troubleshooting information which can help identify
missing information in the submitted MessageCard payload.

---

Note: This commit contains example `logger` object calls
to illustrate the proposed changes mentioned in #10. Before
the associated PR would be merged, those `logger` calls
would be removed and implemented as part of a separate
PR specific to #10.

refs #10, #6 (loosely)
atc0005 referenced this issue in atc0005/go-teams-notify Apr 9, 2020
- Convert Windows/Mac/Linux EOL to Markdown break statements
  - used to provide equivalent Teams-compatible formatting

- Format text as code snippet
  - this inserts leading and trailing ` character to provide
    Markdown string formatting

- Format text as code block
  - this inserts three leading and trailing ` characters to
    provide Markdown code block formatting

- `Try` variants of code formatting functions
  - return formatted string if no errors, otherwise
    return the original string

refs #6 (loosely)
atc0005 referenced this issue in atc0005/go-teams-notify Apr 9, 2020
- Move webhook URL prefixes to constants list
  - export so that client code may reference them
    with any validation work that they perform

- Collect and return user-provided webhook URL alongside
  required URL prefixes to help aid with troubleshooting
  the cause of any errors that they may receive

refs #6 (loosely)
atc0005 added a commit to atc0005/go-teams-notify that referenced this issue Apr 18, 2020
This was intended to go in with the other work for
dasrick#6, but I think this assignment
statement was unintentionaly trimmed alongside the
removal of the package-level logger statements referenced
in dasrick#10.

I caught this when performing a test rebase to audit
what remaining changes in my fork haven't been included
or proposed in the parent/upstream project.
atc0005 added a commit to atc0005/go-teams-notify that referenced this issue Apr 18, 2020
This was intended to go in with the other work for
dasrick#6, but I think this assignment
statement was unintentionally trimmed alongside the
removal of the package-level logger statements referenced
in dasrick#10.

I caught this when performing a test rebase to audit
what remaining changes in my fork haven't been included
or proposed in the parent/upstream project.
@atc0005 atc0005 closed this as completed Aug 26, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants