├── .gitignore
├── .gitmodules
├── LICENSE
├── README.md
├── assets
├── logo
│ ├── azure-functions.png
│ └── dotnet-core-logo.png
├── manager-automation-hq-banner.png
├── manager-automation-lq-banner.jpg
└── screenshots
│ ├── app-insights-trace-log.png
│ ├── azure-functions-app-settings.png
│ ├── functions.png
│ ├── google-chat-id.png
│ ├── in-sprint-alt1.png
│ ├── in-sprint-alt2.png
│ ├── in-sprint-alt3.png
│ ├── last-day-sprint-reminders.png
│ ├── managers-only-reminders.png
│ ├── new-sprint-reminders.png
│ └── pull-request-reminders.png
└── src
├── SampleWorkItem.json
├── TarikGuney.ManagerAutomation.FunctionApp
├── Actors
│ ├── ActivateWorkItemActor.cs
│ ├── DescriptionActor.cs
│ ├── DescriptiveTitlesActor.cs
│ ├── EstimateWorkItemsActor.cs
│ ├── GreatPreviousIterationActor.cs
│ ├── GreatWorkActor.cs
│ ├── LongCodeCompleteActor.cs
│ ├── OpenWorkItemsActor.cs
│ ├── PassedDueWorkItemsActor.cs
│ ├── PendingPullRequestsActor.cs
│ └── StillActiveWorkItemsActor.cs
├── AutoFacModules
│ ├── ConfigurationModule.cs
│ ├── CurrentIterationModule.cs
│ ├── ManagersReportModule.cs
│ └── RetrospectiveModule.cs
├── CommMessages
│ ├── ActorResponse.cs
│ ├── AnalysisCompleteResponse.cs
│ └── StartAnalysisRequest.cs
├── CurrentIterationAutomationFunction.cs
├── Helpers
│ ├── DateDiffHelper.cs
│ └── IterationHelper.cs
├── IterationWorkItemRetrievers
│ ├── AzureDevOpsAllPullRequestsRetriever.cs
│ ├── AzureDevOpsIterationWorkItemsRetriever.cs
│ ├── IIterationWorkItemsRetriever.cs
│ └── IPullRequestsRetriever.cs
├── Managers
│ ├── CurrentIterationManager.cs
│ ├── ProgressReportManager.cs
│ └── RetrospectiveManager.cs
├── ManagersReportAutomationFunction.cs
├── MessageSenders
│ ├── CurrentIterationGoogleChatMessageSender.cs
│ ├── ICurrentIterationMessageSender.cs
│ ├── ILastDayOfCurrentIterationMessageSender.cs
│ ├── IManagersReportMessageSender.cs
│ ├── IMessageSender.cs
│ ├── IRetrospectiveMessageSender.cs
│ ├── LastDayOfCurrentIterationGoogleChatMessageSender.cs
│ ├── ManagersReportGoogleChatMessageSender.cs
│ └── RetrospectiveGoogleChatMessageSender.cs
├── RetrospectiveAutomationFunction.cs
├── SettingsModels
│ ├── AzureDevOpsSettings.cs
│ ├── CurrentIterationInfo.cs
│ ├── DevOpsChatUserMap.cs
│ ├── EngineeringManagerInfo.cs
│ ├── GoogleChatSettings.cs
│ ├── IterationInfo.cs
│ ├── IterationTimeFrame.cs
│ └── PreviousIterationInfo.cs
├── TarikGuney.ManagerAutomation.FunctionApp.csproj
├── WorkItemMessage.cs
└── host.json
├── TarikGuney.ManagerAutomation.UnitTests
├── TarikGuney.ManagerAutomation.UnitTests.csproj
└── UnitTest1.cs
├── TarikGuney.ManagerAutomation.sln
└── global.json
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # Azure Functions localsettings file
5 | local.settings.json
6 |
7 | # User-specific files
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | bld/
24 | [Bb]in/
25 | [Oo]bj/
26 | [Ll]og/
27 |
28 | # Visual Studio 2015 cache/options directory
29 | .vs/
30 | # Uncomment if you have tasks that create the project's static files in wwwroot
31 | #wwwroot/
32 |
33 | # MSTest test Results
34 | [Tt]est[Rr]esult*/
35 | [Bb]uild[Ll]og.*
36 |
37 | # NUNIT
38 | *.VisualState.xml
39 | TestResult.xml
40 |
41 | # Build Results of an ATL Project
42 | [Dd]ebugPS/
43 | [Rr]eleasePS/
44 | dlldata.c
45 |
46 | # DNX
47 | project.lock.json
48 | project.fragment.lock.json
49 | artifacts/
50 |
51 | *_i.c
52 | *_p.c
53 | *_i.h
54 | *.ilk
55 | *.meta
56 | *.obj
57 | *.pch
58 | *.pdb
59 | *.pgc
60 | *.pgd
61 | *.rsp
62 | *.sbr
63 | *.tlb
64 | *.tli
65 | *.tlh
66 | *.tmp
67 | *.tmp_proj
68 | *.log
69 | *.vspscc
70 | *.vssscc
71 | .builds
72 | *.pidb
73 | *.svclog
74 | *.scc
75 |
76 | # Chutzpah Test files
77 | _Chutzpah*
78 |
79 | # Visual C++ cache files
80 | ipch/
81 | *.aps
82 | *.ncb
83 | *.opendb
84 | *.opensdf
85 | *.sdf
86 | *.cachefile
87 | *.VC.db
88 | *.VC.VC.opendb
89 |
90 | # Visual Studio profiler
91 | *.psess
92 | *.vsp
93 | *.vspx
94 | *.sap
95 |
96 | # TFS 2012 Local Workspace
97 | $tf/
98 |
99 | # Guidance Automation Toolkit
100 | *.gpState
101 |
102 | # ReSharper is a .NET coding add-in
103 | _ReSharper*/
104 | *.[Rr]e[Ss]harper
105 | *.DotSettings.user
106 |
107 | # JustCode is a .NET coding add-in
108 | .JustCode
109 |
110 | # TeamCity is a build add-in
111 | _TeamCity*
112 |
113 | # DotCover is a Code Coverage Tool
114 | *.dotCover
115 |
116 | # NCrunch
117 | _NCrunch_*
118 | .*crunch*.local.xml
119 | nCrunchTemp_*
120 |
121 | # MightyMoose
122 | *.mm.*
123 | AutoTest.Net/
124 |
125 | # Web workbench (sass)
126 | .sass-cache/
127 |
128 | # Installshield output folder
129 | [Ee]xpress/
130 |
131 | # DocProject is a documentation generator add-in
132 | DocProject/buildhelp/
133 | DocProject/Help/*.HxT
134 | DocProject/Help/*.HxC
135 | DocProject/Help/*.hhc
136 | DocProject/Help/*.hhk
137 | DocProject/Help/*.hhp
138 | DocProject/Help/Html2
139 | DocProject/Help/html
140 |
141 | # Click-Once directory
142 | publish/
143 |
144 | # Publish Web Output
145 | *.[Pp]ublish.xml
146 | *.azurePubxml
147 | # TODO: Comment the next line if you want to checkin your web deploy settings
148 | # but database connection strings (with potential passwords) will be unencrypted
149 | #*.pubxml
150 | *.publishproj
151 |
152 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
153 | # checkin your Azure Web App publish settings, but sensitive information contained
154 | # in these scripts will be unencrypted
155 | PublishScripts/
156 |
157 | # NuGet Packages
158 | *.nupkg
159 | # The packages folder can be ignored because of Package Restore
160 | **/packages/*
161 | # except build/, which is used as an MSBuild target.
162 | !**/packages/build/
163 | # Uncomment if necessary however generally it will be regenerated when needed
164 | #!**/packages/repositories.config
165 | # NuGet v3's project.json files produces more ignoreable files
166 | *.nuget.props
167 | *.nuget.targets
168 |
169 | # Microsoft Azure Build Output
170 | csx/
171 | *.build.csdef
172 |
173 | # Microsoft Azure Emulator
174 | ecf/
175 | rcf/
176 |
177 | # Windows Store app package directories and files
178 | AppPackages/
179 | BundleArtifacts/
180 | Package.StoreAssociation.xml
181 | _pkginfo.txt
182 |
183 | # Visual Studio cache files
184 | # files ending in .cache can be ignored
185 | *.[Cc]ache
186 | # but keep track of directories ending in .cache
187 | !*.[Cc]ache/
188 |
189 | # Others
190 | ClientBin/
191 | ~$*
192 | *~
193 | *.dbmdl
194 | *.dbproj.schemaview
195 | *.jfm
196 | *.pfx
197 | *.publishsettings
198 | node_modules/
199 | orleans.codegen.cs
200 |
201 | # Since there are multiple workflows, uncomment next line to ignore bower_components
202 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
203 | #bower_components/
204 |
205 | # RIA/Silverlight projects
206 | Generated_Code/
207 |
208 | # Backup & report files from converting an old project file
209 | # to a newer Visual Studio version. Backup files are not needed,
210 | # because we have git ;-)
211 | _UpgradeReport_Files/
212 | Backup*/
213 | UpgradeLog*.XML
214 | UpgradeLog*.htm
215 |
216 | # SQL Server files
217 | *.mdf
218 | *.ldf
219 |
220 | # Business Intelligence projects
221 | *.rdl.data
222 | *.bim.layout
223 | *.bim_*.settings
224 |
225 | # Microsoft Fakes
226 | FakesAssemblies/
227 |
228 | # GhostDoc plugin setting file
229 | *.GhostDoc.xml
230 |
231 | # Node.js Tools for Visual Studio
232 | .ntvs_analysis.dat
233 |
234 | # Visual Studio 6 build log
235 | *.plg
236 |
237 | # Visual Studio 6 workspace options file
238 | *.opt
239 |
240 | # Visual Studio LightSwitch build output
241 | **/*.HTMLClient/GeneratedArtifacts
242 | **/*.DesktopClient/GeneratedArtifacts
243 | **/*.DesktopClient/ModelManifest.xml
244 | **/*.Server/GeneratedArtifacts
245 | **/*.Server/ModelManifest.xml
246 | _Pvt_Extensions
247 |
248 | # Paket dependency manager
249 | .paket/paket.exe
250 | paket-files/
251 |
252 | # FAKE - F# Make
253 | .fake/
254 |
255 | # JetBrains Rider
256 | .idea/
257 | *.sln.iml
258 |
259 | # CodeRush
260 | .cr/
261 |
262 | # Python Tools for Visual Studio (PTVS)
263 | __pycache__/
264 | *.pyc
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "src/TarikGuney.ManagerAutomation.FunctionApp/secrets"]
2 | path = src/TarikGuney.ManagerAutomation.FunctionApp/secrets
3 | url = git@github.com:tarikguney/manager-automation-secrets
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Developed with the following technologies by [@tarikguney](https://github.com/tarikguney)
2 |
3 |  
4 |
5 | 
6 |
7 | Table of Contents
8 | =================
9 |
10 | * [Table of Contents](#table-of-contents)
11 | * [What is it?](#what-is-it)
12 | * [What's new in Version 2](#whats-new-in-version-2)
13 | * [Underlying Design Principle](#underlying-design-principle)
14 | * [How does it look?](#how-does-it-look)
15 | * [In-Sprint action reminders](#in-sprint-action-reminders)
16 | * [End-of-sprint action reminders](#end-of-sprint-action-reminders)
17 | * [New Sprint action reminders](#new-sprint-action-reminders)
18 | * [Pull Request Reminders](#pull-request-reminders)
19 | * [Managers-only reminders](#managers-only-reminders)
20 | * [Reporting](#reporting)
21 | * [What's being logged?](#whats-being-logged)
22 | * [Real-world log samples from the console](#real-world-log-samples-from-the-console)
23 | * [Where to find the logs in Application Insights?](#where-to-find-the-logs-in-application-insights)
24 | * [The Complete List of Reminders](#the-complete-list-of-reminders)
25 | * [Current Iteration Automation](#current-iteration-automation)
26 | * [End of the Sprint Reminders](#end-of-the-sprint-reminders)
27 | * [Retrospective Automation](#retrospective-automation)
28 | * [Managers-only Automation](#managers-only-automation)
29 | * [Setup and Configuration](#setup-and-configuration)
30 | * [Dependencies](#dependencies)
31 | * [Publishing to Azure Functions](#publishing-to-azure-functions)
32 | * [App Settings](#app-settings)
33 | * [Available settings](#available-settings)
34 | * [Where to put the app settings?](#where-to-put-the-app-settings)
35 | * [Explanation of each setting](#explanation-of-each-setting)
36 | * [AzureDevOps__ApiKey](#azuredevops__apikey)
37 | * [AzureDevOps__Organization](#azuredevops__organization)
38 | * [AzureDevOps__Project](#azuredevops__project)
39 | * [AzureDevOps__Team](#azuredevops__team)
40 | * [AzureDevOpsUsersMapToGoogleChat](#azuredevopsusersmaptogooglechat)
41 | * [EngineeringManagerInfo__AzureDevOpsEmail](#engineeringmanagerinfo__azuredevopsemail)
42 | * [EngineeringManagerInfo__GoogleChatUserId](#engineeringmanagerinfo__googlechatuserid)
43 | * [EngineeringManagerInfo__ManagerRemindersGoogleWebhookUrl](#engineeringmanagerinfo__managerremindersgooglewebhookurl)
44 | * [GoogleChat__WebhookUrl](#googlechat__webhookurl)
45 | * [WEBSITE_TIME_ZONE](#website_time_zone)
46 | * [Finding Google Chat Id (UserId)](#finding-google-chat-id-userid)
47 | * [FAQ](#faq)
48 |
49 |
50 |
51 |
52 |
53 | # What is it?
54 | **As an engineering manager, you realize that a lot of your time goes to repetitive tasks which also include guiding your team in keeping the sprint board up-to-date with enough level of details for an accurate work tracking.** There could be many reasons why you find yourself in this situation, but regardless of the reason, Manager Automation will take some of that burden off your shoulders. It will continuously analyze the current Sprint's board. It will remind the team of the missing information in the form of actionable reminders that are important for the smooth execution of every Sprint. **Note that these reminders play an essential role in guiding the team and new hires towards the expectations of the team and company culture.**
55 | # What's new in Version 2
56 |
57 | There is already a project of mine named [Manager Automation](https://github.com/tarikguney/manager-automation), but as I was learning [Akka.NET](https://getakka.net), I decided to re-write the entire project in Akka. The original version was written in [TPL Dataflow](https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/dataflow-task-parallel-library), which is a great library; but overall, I found the [Actor Model](https://en.wikipedia.org/wiki/Actor_model) constructs more straightforward in Akka than TPL Dataflow. Akka also offers more constructs for the development teams to focus on the business results rather than re-inventing various existing concepts. Some of the areas Akka.NET offers better development support that I am interested in this project are:
58 |
59 | 1. [Dependency Injection](https://getakka.net/articles/actors/dependency-injection.html)
60 |
61 | 2. [Streaming](https://getakka.net/articles/streams/introduction.html)
62 |
63 | 3. [Testing Actors](https://getakka.net/articles/actors/testing-actor-systems.html)
64 |
65 | My newly acquired experience from this re-write of this project in Akka.NET will be incorporated into [Projekt Hive](https://github.com/tarikguney/projekt-hive), which is planned to be a complete re-imagination of [Manager Automation](https://www.github.com/tarikguney/manager-automation) from the ground up.
66 |
67 | If you are interested in Akka.NET and want to see a project being re-written with actors, then follow this project. You can compare the original commit to the latest commit to see the evolution of the project. It is probably a rare moment to see a live project, that is originally written with TPL Dataflow, being re-written in Akka.NET. So, enjoy, and don't forget to leave your comments and suggestions on the [Discussions](https://github.com/tarikguney/manager-automation-v2/discussions) page.
68 |
69 | # Underlying Design Principle
70 | The communication of the reminders in the solution is intentionally done in a public team chat room. Besides being public, the messages are kept straightforward too. It communicates the required actions through Google Chat (and Slack, etc., in the future) in simple and concise words. It is visible and repetitive, as these are perhaps some of the most effective factors to incorporate cultural elements into a team's daily life. Rare communication of hard-to-remember tasks will not make it into the team culture early enough. They have to be communicated frequently in a place where they are most visible to make them easy to follow by the team to become a part of the team culture.
71 |
72 | # How does it look?
73 |
74 | There are some screenshots of the messages below for you to see what's being communicated. Note that the source code will be the best place to check for the full list of messages and communication for now.
75 |
76 | ## In-Sprint action reminders
77 | Manager Automation service sends daily reminders as demonstrated in the screenshots below. These series of reminders ensure the necessary actions being taken by the team members. The reminders use Google Chat's name mentioning feature to help the targeted team members to get on-time and relevant notifications through the Google Chat.
78 |
79 |
80 |
81 |
82 |
83 | ## End-of-sprint action reminders
84 | Manager Automation service embeds specific situational facts in the reminder text. For example, it changes its greeting when it is the last day of the Sprint to draw people's attention to the fact that the Sprint is about to end, and there might be some missing work in the Sprint. It also guides the team members on how the incomplete work must be handled based on the time available and committed time for the work item estimated by the engineer.
85 |
86 |
87 |
88 | ## New Sprint action reminders
89 | When it is the first day of the new Sprint, the automation service will analyze the previous Sprint one more time and bring up any issues that are left unresolved.
90 |
91 |
92 |
93 | ## Pull Request Reminders
94 |
95 | Reading the existing PRs from the Azure DevsOps, and sending the engineering team daily reminders to ensure the developed code gets merged into the main branch. This increases the awareness of work completion among the team. At the end of the day, it is only the merged code that actually turns into a business value.
96 |
97 |
98 |
99 | ## Managers-only reminders
100 | As an engineering manager, it is often crucial to be aware of the work's progression on time and catch the delay before becoming a big problem. There might be many reasons why work is delayed. For instance, engineering might be blocked. Regardless of the reason, if a task is taking an unreasonable amount of time, you will need to reach out and see what's going on and how you can help the team member with the progression of the work. The Manager Automation service collects this information for you -- the engineering manager -- and reports them daily in a private Google Chat room.
101 |
102 |
103 |
104 | # Reporting
105 |
106 | Reporting is one of the most important capabilities of the Manager Automation tool. The daily reminders are also sent to the [**Azure Application Insights**](https://docs.microsoft.com/en-us/azure/azure-monitor/app/app-insights-overview) for a further review by the team and the engineering manager. You can write up reports using these logs to better understand the team's performance and improvement over time. It is up to you when it comes to how to use these reporting capabilities, but a recommended approach is to help the team grow by helping them see what areas they need to work to improve. For instance, if they are not good at estimating the work items, they can objectively see this problem area in the reports and work to improve it over a course of time.
107 |
108 | The reporting is a fairly new addition to the manager automation, hence, the documentation lacks enough level of details and screenshots. However, it is simple to use when you integrate the Application Insights to your Azure Functions application. Ensure that you select an Application Insights instance (or create a new one) when you first create your function application.
109 |
110 | ## What's being logged?
111 |
112 | These are the logs you will see in the Application Insights logs:
113 |
114 | ```
115 | BOARD: Missing description for \"{workItemId}:{workItemTitle}\". Assigned to {userEmail} in {currentIteration}.
116 |
117 | BOARD: Unclear title for \"{workItemId}:{workItemTitle}\". Assigned to {userEmail} in {currentIteration}.
118 |
119 | BOARD: Missing story point for \"{workItemId}:{workItemTitle}\". Assigned to {userEmail} in {currentIteration}.
120 |
121 | BOARD: Closed everything from the previous sprint by the first day of the current sprint {currentIteration}. Assigned to {userEmail}.
122 |
123 | BOARD: Closed everything in the current sprint {currentIteration}. Assigned to {userEmail}.
124 |
125 | BOARD: Pending in incomplete state of {currentState} for {pendingForDays} days. Story \"{workItemId}:{workItemTitle}\". Assigned to {userEmail} in {currentIteration}.
126 |
127 | BOARD: Still open in {currentState} state. Story \"{workItemId}:{workItemTitle}\". Assigned to {userEmail} in {currentIteration}."
128 |
129 | BOARD: Still in active state. Story \"{workItemId}:{workItemTitle}\". Assigned to {userEmail} in {currentIteration}.
130 |
131 | CODE: Pending pull request \"{pullRequestTitle}:{pullRequestId}\". Created by {createdBy} on {createdDate}.
132 | ```
133 | Note that all these logs start with `BOARD` to help you distinguish these log entries from the other sort of logs easily.
134 |
135 | The log messages above are taken directly from the code. As you might have guessed, they follow [**structural logging**](https://softwareengineering.stackexchange.com/a/312586/3613) pattern; meaning that they can easily be parsed and parameterized with the metadata they carry for each fields as represented by `{ }` as seen in `{workItemId}`.
136 |
137 | ## Real-world log samples from the console
138 |
139 | Samples from console environment:
140 | ```
141 | [2020-12-26T21:36:32.640Z] BOARD: Missing story point for "12:Create the profile edit page". Assigned to atarikguney@gmail.com in Sprint 3.
142 | [2020-12-26T21:36:32.643Z] BOARD: Pending in incomplete state of Resolved for 28 days. Story "19:This is an active item". Assigned to atarikguney@gmail.com in Sprint 3.
143 | ```
144 |
145 | More documentation and examples will follow as this is one of the most critical functionalities of Manager Automation tool.
146 |
147 | ## Where to find the logs in Application Insights?
148 |
149 | The log entries above are logged as trace. This is default log type in Azure Functions runtime. Therefore, when you go to the Log tab on Azure Portal, search the log entries in the trace section. You can also create dashboard for more convenient view. Check out the image below, it will show you how to access the Log and how to perform a simple search to find the relevant log entries for further reporting needs:
150 |
151 |
152 |
153 | # The Complete List of Reminders
154 | This function application consists of three primary functions:
155 | 1. Current Iteration Automation
156 | 1. End of iteration Automation
157 | 1. Retrospective Automation
158 | 1. Managers-only Reminders
159 |
160 | ## Current Iteration Automation
161 | Most of the reminders go out as part of the current iteration automation executions. This automation analyses the current iteration (aka. Sprint) using Azure DevOps API from various points and sends reminders to the responsible parties to make sure they are addressed. These checkpoints are:
162 | - Missing description in work items (defects and user stories)
163 | - Indescriptive work item titles
164 | - Work items without story points
165 | - Work items in Resolve state for more than 12 hours
166 | - Assigned but not activated work items. Reminds the responsible parties to activate the work item they are working.
167 | - Congratulate the ones with all closed work items.
168 |
169 | These reminders are sent out twice every day, one in the morning and one in the afternoon.
170 |
171 | ## End of the Sprint Reminders
172 | On the last day of the Sprint, the reminders change. In addition to the ones above, it also automates:
173 |
174 | - If there are work items that are still active, it reminds the possible action items.
175 |
176 | ## Retrospective Automation
177 | On the first day of the Sprint, this automation checks if there is any remaining work from the previous Sprint. It runs the existing checks and reports them differently, which is more appropriate for the first day of the next Sprint.
178 |
179 | ## Managers-only Automation
180 |
181 | - Due date reminders.
182 |
183 | # Setup and Configuration
184 | ## Dependencies
185 | This project is developed for **[Azure Functions](https://azure.microsoft.com/en-us/services/functions) with [**.NET Core 3.1**](https://dotnet.microsoft.com/)**. Hence, some basic knowledge in this technology stack is helpful. Also, this service assumes that you are using **[Azure DevOps](https://dev.azure.com)** as the project planning/tracking environment and **[Google Chat](https://chat.google.com)** as the communication medium among your team members.
186 | ## Publishing to Azure Functions
187 | Deploying this source code to the Azure Function is easy. This project uses the Library version of Azure Functions. Check out these steps to learn how to publish Azure Functions: [Publish to Azure](https://docs.microsoft.com/en-us/azure/azure-functions/functions-develop-vs#publish-to-azure).
188 |
189 | Once you deploy this function app to Azure Functions, these are the functions that will appear on the Azure Portal:
190 |
191 |
192 |
193 | ## App Settings
194 |
195 | To complete app settings, you need various pieces of information from Azure DevOps and Google Chat.
196 |
197 | In addition to the default app settings that come with the Azure Function creation, there are some custom settings that the source code depends on as follows. All of them are required!
198 |
199 | ### Available settings
200 |
201 | ```json
202 | [
203 | {
204 | "name": "AzureDevOps__ApiKey",
205 | "value": "PERSONAL-ACCESS-TOKEN-FROM-AZURE-DEVOPS",
206 | "slotSetting": false
207 | },
208 | {
209 | "name": "AzureDevOps__Organization",
210 | "value": "ORGANIZATION-NAME-OF-AZURE-DEVOPS",
211 | "slotSetting": false
212 | },
213 | {
214 | "name": "AzureDevOps__Project",
215 | "value": "PROJECT-NAME-FROM-AZURE-DEVOPS",
216 | "slotSetting": false
217 | },
218 | {
219 | "name": "AzureDevOps__Team",
220 | "value": "TEAM-NAME-FROM-AZURE-DEVOPS",
221 | "slotSetting": false
222 | },
223 | {
224 | "name": "AzureDevOpsUsersMapToGoogleChat",
225 | "value": "AZURE-DEVOPS-USER1-EMAIL:GOOGLE-CHAT-ID-1;AZURE-DEVOPS-USER2-EMAIL:GOOGLE-CHAT-ID-2",
226 | "slotSetting": false
227 | },
228 | {
229 | "name": "EngineeringManagerInfo__AzureDevOpsEmail",
230 | "value": "ENGINEERING-MANAGER-EMAIL-FROM-AZURE-DEVOPS",
231 | "slotSetting": false
232 | },
233 | {
234 | "name": "EngineeringManagerInfo__GoogleChatUserId",
235 | "value": "ENGINEERING-MANAGER-GOOGLE-CHAT-ID",
236 | "slotSetting": false
237 | },
238 | {
239 | "name": "EngineeringManagerInfo__ManagerRemindersGoogleWebhookUrl",
240 | "value": "MANAGERS-ONLY-REMINDERS-ROOM-WEBHOOK-URL",
241 | "slotSetting": false
242 | },
243 | {
244 | "name": "GoogleChat__WebhookUrl",
245 | "value": "GOOGLE-CHAT-ROOM-WEBHOOK-URL",
246 | "slotSetting": false
247 | },
248 | {
249 | "name": "WEBSITE_TIME_ZONE",
250 | "value": "Mountain Standard Time",
251 | "slotSetting": false
252 | }
253 | ]
254 | ```
255 |
256 | ### Where to put the app settings?
257 |
258 | Don't store the application settings in `appsettings.json` file. I recommend `appsettings.json` file only for local development since Azure Functions App have a better place to put the configuration settings via Azure Portal. Visit the `Configuration` link in your Functions instance as shown below:
259 |
260 |
261 |
262 | This way, you don't have re-publish the function app when you change a setting.
263 |
264 | ### Explanation of each setting
265 |
266 | Note that the double underscore (`__`) in the setting names have a [special meaning in .NET Core configurations](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-5.0#environment-variables). Basically, they are used to group the settings in the environment variables.
267 |
268 | #### `AzureDevOps__ApiKey`
269 |
270 | This is your personal access token. Check out this page to see how to get it from Azure DevOps: [**Create a PAT**](https://docs.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate?view=azure-devops&tabs=preview-page#create-a-pat). When creating your PAT, you can define the scopes this token can access. Keep it limited to the following scopes for most safety:
271 |
272 | 1. Choose “Custom Defined”
273 | 2. Code > Read
274 | 3. Project and Team > Read
275 | 4. Work Items > Read
276 |
277 | Then, follow these steps to generate the API key you need for this setting:
278 | 1. Use this format: `:personal-access-token` --> Don't forget to add the colon at the beginning.
279 | 2. Encode it with Base64String
280 | 3. Use the resulting string as the value for this setting.
281 |
282 | #### `AzureDevOps__Organization`
283 |
284 | This is the organization of your Azure DevOps. You can infer this value from the URL: `https://dev.azure.com/{organization}`. The first segment in the path of the DevOps URL is the organization. Simply use that value.
285 |
286 | #### `AzureDevOps__Project`
287 |
288 | You can create multiple projects in Azure DevOps, and this setting requires one of them under which you sprint boards, etc. are all defined. When you go to your organization URL `dev.azure.com/{organization}`, you will be presented with the projects. Simply choose the one you desire and copy their names as they appear on that screen to use as the value of this setting.
289 |
290 | #### `AzureDevOps__Team`
291 |
292 | You can define teams in Azure DevOps, and boards are associated with teams. Use that team name as the value for this setting. Check out this page to learn more about teams: [**Add team, go from one default team to others**](https://docs.microsoft.com/en-us/azure/devops/organizations/settings/add-teams?view=azure-devops&tabs=preview-page)
293 |
294 | #### `AzureDevOpsUsersMapToGoogleChat`
295 |
296 | In order to determine whom to send the message to, there should be some mapping between Azure DevOps users and Google Chat users. Google Chat Webhook API only works with Google Chat User Ids, and this setting is to map between these values. Map all of the team members in this setting as the recommended approach. Otherwise, some members may not be properly mentioned in Google Chat. This value accepts multiple mappings separated by semi-colon (`;`). Check out this link to see how to extract Google Chat Id: [Finding Google Chat Id (UserId)](#finding-google-chat-id-userid)
297 |
298 | `AZURE-DEVOPS-USER1-EMAIL:GOOGLE-CHAT-ID-1` would translate to `michael.smith@gmail.com:2333181726262`in a real-world setting. Another example with multiple mappings would be as follows: `michael.smith@gmail.com:2333181726262;tarik.guney@fakeaddress.com:23344556311`. Note the `;` as the separator of these mappings. You can add as many as you want.
299 |
300 | #### `EngineeringManagerInfo__AzureDevOpsEmail`
301 |
302 | Team and project/engineering managers receive different messages. Therefore, project/engineering manager information is asked separately. Use your DevOps email as the value of this setting.
303 |
304 | #### `EngineeringManagerInfo__GoogleChatUserId`
305 |
306 | Similar to other Google Chat Ids, use the Google Chat Id of the project/engineering manager as the value of this settings. Find out more at [Finding Google Chat Id (UserId)](#finding-google-chat-id-userid)
307 |
308 | #### `EngineeringManagerInfo__ManagerRemindersGoogleWebhookUrl`
309 |
310 | Google Chat has webhooks to receive messages through. It is really easy to create Webhook URLs through Google Chat as explained here [Using incoming webhooks](https://developers.google.com/hangouts/chat/how-tos/webhooks).
311 |
312 | This particular setting is asking for a private room webhook to send [Managers-only Automation](#managers-only-automation) messages.
313 |
314 | #### `GoogleChat__WebhookUrl`
315 |
316 | Google Chat has webhooks to receive messages through. It is really easy to create Webhook URLs through Google Chat as explained here [Using incoming webhooks](https://developers.google.com/hangouts/chat/how-tos/webhooks).
317 |
318 | This particular setting is asking for the team room webhook to send automation messages.
319 |
320 | #### `WEBSITE_TIME_ZONE`
321 |
322 | This is a pre-defined setting understood by Azure Functions, and is required for determining the time zone for the automation to determine how to calculate time and days to send the messages out. You can find all of the available options here: [Time Zones](https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-vista/cc749073(v=ws.10)#time-zones). Use the values from the left side as they appear, with spaces and such. For example: `Mountain Standard Time`.
323 |
324 | ### Finding Google Chat Id (UserId)
325 |
326 | Google Chat Id is used in the following configuration settings and is important for notifying the right team members through Google Chat.
327 |
328 | ```json
329 | {
330 | "name": "AzureDevOpsUsersMapToGoogleChat",
331 | "value": "AZURE-DEVOPS-USER1-EMAIL:GOOGLE-CHAT-ID-1;AZURE-DEVOPS-USER2-EMAIL:GOOGLE-CHAT-ID-2",
332 | "slotSetting": false
333 | },
334 | {
335 | "name": "EngineeringManagerInfo__GoogleChatUserId",
336 | "value": "ENGINEERING-MANAGER-GOOGLE-CHAT-ID",
337 | "slotSetting": false
338 | }
339 | ```
340 | It is not super straightforward and intuitive to find out what this value is for each team member. It is not an an exposed value on the Google Chat UI; therefore, you need to use the tools like Google Chrome Developer Tools to extract it. You have to copy the numbers next to `user/human/` value in `data-member-id` HTML attribute as shown in the screenshot below:
341 |
342 |
343 |
344 | # FAQ
345 |
346 | **Where are the unit tests?**
347 |
348 | I originally developed this tool as a small utility service to automate some of my work, but it grew quickly as I realize the potential of this tool. I will be adding unit tests later on.
349 |
350 | **Where did the idea come from?**
351 |
352 | From my own experiences. I noticed that I could not scale my time if I didn't automate some of my daily tasks, and this project was born. In general, I like developing tools that make my live easier, and I love tools that offer practical impact and improvements to my daily life. You can check out my other projects at [github.com/tarikguney](https://github.com/tarikguney) to explore my other tools that were developed with this simple idea in mind. Since, there are lots of people like myself sharing the same challenges, developing a tool for myself ends up helping a lot of other people out there.
353 |
354 |
355 | **What's the roadmap/future of this project?**
356 |
357 | I have learned quite a bit from this project, and I will be maintaining this project time to time. But, my next project, that will take this simple idea a lot farther, will be [**Projekt Hive**](https://github.com/tarikguney/projekt-hive). I will be incorporating what I have learned so far and more into Projekt Hive.
358 |
359 | **How can I contribute?**
360 |
361 | There are multiple areas where contribution is needed:
362 |
363 | 1. Adding more message targets like Slack, Discord, etc. similar to Google Chat.
364 | 2. Adding unit tests!
365 | 3. Bringing the project to a more unit testable state. I used .NET DataFlow pattern which is little challenging when it comes to Dependency Injection. However, I like to keep the actor pattern in place since it is really suitable for this type of projects.
366 | 4. Test it and log bugs in the issues page
367 |
368 | Since this is my personal side project, I cannot immediately answer questions or review PRs but I will do my best. So, please feel free to contribute but just be a little patient with me.
369 |
370 |
371 |
372 |
--------------------------------------------------------------------------------
/assets/logo/azure-functions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tarikguney/manager-automation-v2/ae406c0346baec826a4af27851db484bde05cc69/assets/logo/azure-functions.png
--------------------------------------------------------------------------------
/assets/logo/dotnet-core-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tarikguney/manager-automation-v2/ae406c0346baec826a4af27851db484bde05cc69/assets/logo/dotnet-core-logo.png
--------------------------------------------------------------------------------
/assets/manager-automation-hq-banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tarikguney/manager-automation-v2/ae406c0346baec826a4af27851db484bde05cc69/assets/manager-automation-hq-banner.png
--------------------------------------------------------------------------------
/assets/manager-automation-lq-banner.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tarikguney/manager-automation-v2/ae406c0346baec826a4af27851db484bde05cc69/assets/manager-automation-lq-banner.jpg
--------------------------------------------------------------------------------
/assets/screenshots/app-insights-trace-log.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tarikguney/manager-automation-v2/ae406c0346baec826a4af27851db484bde05cc69/assets/screenshots/app-insights-trace-log.png
--------------------------------------------------------------------------------
/assets/screenshots/azure-functions-app-settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tarikguney/manager-automation-v2/ae406c0346baec826a4af27851db484bde05cc69/assets/screenshots/azure-functions-app-settings.png
--------------------------------------------------------------------------------
/assets/screenshots/functions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tarikguney/manager-automation-v2/ae406c0346baec826a4af27851db484bde05cc69/assets/screenshots/functions.png
--------------------------------------------------------------------------------
/assets/screenshots/google-chat-id.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tarikguney/manager-automation-v2/ae406c0346baec826a4af27851db484bde05cc69/assets/screenshots/google-chat-id.png
--------------------------------------------------------------------------------
/assets/screenshots/in-sprint-alt1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tarikguney/manager-automation-v2/ae406c0346baec826a4af27851db484bde05cc69/assets/screenshots/in-sprint-alt1.png
--------------------------------------------------------------------------------
/assets/screenshots/in-sprint-alt2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tarikguney/manager-automation-v2/ae406c0346baec826a4af27851db484bde05cc69/assets/screenshots/in-sprint-alt2.png
--------------------------------------------------------------------------------
/assets/screenshots/in-sprint-alt3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tarikguney/manager-automation-v2/ae406c0346baec826a4af27851db484bde05cc69/assets/screenshots/in-sprint-alt3.png
--------------------------------------------------------------------------------
/assets/screenshots/last-day-sprint-reminders.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tarikguney/manager-automation-v2/ae406c0346baec826a4af27851db484bde05cc69/assets/screenshots/last-day-sprint-reminders.png
--------------------------------------------------------------------------------
/assets/screenshots/managers-only-reminders.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tarikguney/manager-automation-v2/ae406c0346baec826a4af27851db484bde05cc69/assets/screenshots/managers-only-reminders.png
--------------------------------------------------------------------------------
/assets/screenshots/new-sprint-reminders.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tarikguney/manager-automation-v2/ae406c0346baec826a4af27851db484bde05cc69/assets/screenshots/new-sprint-reminders.png
--------------------------------------------------------------------------------
/assets/screenshots/pull-request-reminders.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tarikguney/manager-automation-v2/ae406c0346baec826a4af27851db484bde05cc69/assets/screenshots/pull-request-reminders.png
--------------------------------------------------------------------------------
/src/SampleWorkItem.json:
--------------------------------------------------------------------------------
1 | {
2 | "count": 2,
3 | "value": [
4 | {
5 | "id": 11,
6 | "rev": 3,
7 | "fields": {
8 | "System.Id": 11,
9 | "System.AreaId": 14,
10 | "System.AreaPath": "Azure Functions",
11 | "System.TeamProject": "Azure Functions",
12 | "System.NodeName": "Azure Functions",
13 | "System.AreaLevel1": "Azure Functions",
14 | "System.Rev": 3,
15 | "System.AuthorizedDate": "2020-10-16T16:53:54.473Z",
16 | "System.RevisedDate": "9999-01-01T00:00:00Z",
17 | "System.IterationId": 15,
18 | "System.IterationPath": "Azure Functions\\Sprint 2",
19 | "System.IterationLevel1": "Azure Functions",
20 | "System.IterationLevel2": "Sprint 2",
21 | "System.WorkItemType": "User Story",
22 | "System.State": "New",
23 | "System.Reason": "New",
24 | "System.AssignedTo": {
25 | "displayName": "Tarik Guney",
26 | "url": "https://spsprodcus2.vssps.visualstudio.com/A53d7da5a-3070-4ed6-94d8-67304e463b68/_apis/Identities/a3588167-b8df-4070-b04a-b7ce837e699c",
27 | "_links": {
28 | "avatar": {
29 | "href": "https://dev.azure.com/atarikguney/_apis/GraphProfile/MemberAvatars/msa.YzFhZjAzZjYtNDY0Ny03YWE5LWEwNGQtNzVlMzRlMzE1N2Vh"
30 | }
31 | },
32 | "id": "a3588167-b8df-4070-b04a-b7ce837e699c",
33 | "uniqueName": "atarikguney@gmail.com",
34 | "imageUrl": "https://dev.azure.com/atarikguney/_apis/GraphProfile/MemberAvatars/msa.YzFhZjAzZjYtNDY0Ny03YWE5LWEwNGQtNzVlMzRlMzE1N2Vh",
35 | "descriptor": "msa.YzFhZjAzZjYtNDY0Ny03YWE5LWEwNGQtNzVlMzRlMzE1N2Vh"
36 | },
37 | "System.CreatedDate": "2020-10-14T16:04:19.173Z",
38 | "System.CreatedBy": {
39 | "displayName": "Tarik Guney",
40 | "url": "https://spsprodcus2.vssps.visualstudio.com/A53d7da5a-3070-4ed6-94d8-67304e463b68/_apis/Identities/a3588167-b8df-4070-b04a-b7ce837e699c",
41 | "_links": {
42 | "avatar": {
43 | "href": "https://dev.azure.com/atarikguney/_apis/GraphProfile/MemberAvatars/msa.YzFhZjAzZjYtNDY0Ny03YWE5LWEwNGQtNzVlMzRlMzE1N2Vh"
44 | }
45 | },
46 | "id": "a3588167-b8df-4070-b04a-b7ce837e699c",
47 | "uniqueName": "atarikguney@gmail.com",
48 | "imageUrl": "https://dev.azure.com/atarikguney/_apis/GraphProfile/MemberAvatars/msa.YzFhZjAzZjYtNDY0Ny03YWE5LWEwNGQtNzVlMzRlMzE1N2Vh",
49 | "descriptor": "msa.YzFhZjAzZjYtNDY0Ny03YWE5LWEwNGQtNzVlMzRlMzE1N2Vh"
50 | },
51 | "System.ChangedDate": "2020-10-16T16:53:54.473Z",
52 | "System.ChangedBy": {
53 | "displayName": "Tarik Guney",
54 | "url": "https://spsprodcus2.vssps.visualstudio.com/A53d7da5a-3070-4ed6-94d8-67304e463b68/_apis/Identities/a3588167-b8df-4070-b04a-b7ce837e699c",
55 | "_links": {
56 | "avatar": {
57 | "href": "https://dev.azure.com/atarikguney/_apis/GraphProfile/MemberAvatars/msa.YzFhZjAzZjYtNDY0Ny03YWE5LWEwNGQtNzVlMzRlMzE1N2Vh"
58 | }
59 | },
60 | "id": "a3588167-b8df-4070-b04a-b7ce837e699c",
61 | "uniqueName": "atarikguney@gmail.com",
62 | "imageUrl": "https://dev.azure.com/atarikguney/_apis/GraphProfile/MemberAvatars/msa.YzFhZjAzZjYtNDY0Ny03YWE5LWEwNGQtNzVlMzRlMzE1N2Vh",
63 | "descriptor": "msa.YzFhZjAzZjYtNDY0Ny03YWE5LWEwNGQtNzVlMzRlMzE1N2Vh"
64 | },
65 | "System.AuthorizedAs": {
66 | "displayName": "Tarik Guney",
67 | "url": "https://spsprodcus2.vssps.visualstudio.com/A53d7da5a-3070-4ed6-94d8-67304e463b68/_apis/Identities/a3588167-b8df-4070-b04a-b7ce837e699c",
68 | "_links": {
69 | "avatar": {
70 | "href": "https://dev.azure.com/atarikguney/_apis/GraphProfile/MemberAvatars/msa.YzFhZjAzZjYtNDY0Ny03YWE5LWEwNGQtNzVlMzRlMzE1N2Vh"
71 | }
72 | },
73 | "id": "a3588167-b8df-4070-b04a-b7ce837e699c",
74 | "uniqueName": "atarikguney@gmail.com",
75 | "imageUrl": "https://dev.azure.com/atarikguney/_apis/GraphProfile/MemberAvatars/msa.YzFhZjAzZjYtNDY0Ny03YWE5LWEwNGQtNzVlMzRlMzE1N2Vh",
76 | "descriptor": "msa.YzFhZjAzZjYtNDY0Ny03YWE5LWEwNGQtNzVlMzRlMzE1N2Vh"
77 | },
78 | "System.PersonId": 72169719,
79 | "System.Watermark": 23,
80 | "System.CommentCount": 0,
81 | "System.Title": "Create the user profile page",
82 | "System.BoardColumn": "New",
83 | "System.BoardColumnDone": false,
84 | "Microsoft.VSTS.Common.StateChangeDate": "2020-10-14T16:04:19.173Z",
85 | "Microsoft.VSTS.Common.Priority": 2,
86 | "Microsoft.VSTS.Common.ValueArea": "Business",
87 | "WEF_CD17F1AD5A3A4147BAE15B6D7B4EBF5A_System.ExtensionMarker": true,
88 | "WEF_CD17F1AD5A3A4147BAE15B6D7B4EBF5A_Kanban.Column": "New",
89 | "WEF_CD17F1AD5A3A4147BAE15B6D7B4EBF5A_Kanban.Column.Done": false
90 | },
91 | "url": "https://dev.azure.com/atarikguney/5008ed37-39a9-4c99-bbdc-fcfa9282f424/_apis/wit/workItems/11"
92 | },
93 | {
94 | "id": 12,
95 | "rev": 6,
96 | "fields": {
97 | "System.Id": 12,
98 | "System.AreaId": 14,
99 | "System.AreaPath": "Azure Functions",
100 | "System.TeamProject": "Azure Functions",
101 | "System.NodeName": "Azure Functions",
102 | "System.AreaLevel1": "Azure Functions",
103 | "System.Rev": 6,
104 | "System.AuthorizedDate": "2020-10-16T16:53:54.473Z",
105 | "System.RevisedDate": "9999-01-01T00:00:00Z",
106 | "System.IterationId": 15,
107 | "System.IterationPath": "Azure Functions\\Sprint 2",
108 | "System.IterationLevel1": "Azure Functions",
109 | "System.IterationLevel2": "Sprint 2",
110 | "System.WorkItemType": "User Story",
111 | "System.State": "New",
112 | "System.Reason": "New",
113 | "System.AssignedTo": {
114 | "displayName": "Tarik Guney",
115 | "url": "https://spsprodcus2.vssps.visualstudio.com/A53d7da5a-3070-4ed6-94d8-67304e463b68/_apis/Identities/a3588167-b8df-4070-b04a-b7ce837e699c",
116 | "_links": {
117 | "avatar": {
118 | "href": "https://dev.azure.com/atarikguney/_apis/GraphProfile/MemberAvatars/msa.YzFhZjAzZjYtNDY0Ny03YWE5LWEwNGQtNzVlMzRlMzE1N2Vh"
119 | }
120 | },
121 | "id": "a3588167-b8df-4070-b04a-b7ce837e699c",
122 | "uniqueName": "atarikguney@gmail.com",
123 | "imageUrl": "https://dev.azure.com/atarikguney/_apis/GraphProfile/MemberAvatars/msa.YzFhZjAzZjYtNDY0Ny03YWE5LWEwNGQtNzVlMzRlMzE1N2Vh",
124 | "descriptor": "msa.YzFhZjAzZjYtNDY0Ny03YWE5LWEwNGQtNzVlMzRlMzE1N2Vh"
125 | },
126 | "System.CreatedDate": "2020-10-14T16:04:41.533Z",
127 | "System.CreatedBy": {
128 | "displayName": "Tarik Guney",
129 | "url": "https://spsprodcus2.vssps.visualstudio.com/A53d7da5a-3070-4ed6-94d8-67304e463b68/_apis/Identities/a3588167-b8df-4070-b04a-b7ce837e699c",
130 | "_links": {
131 | "avatar": {
132 | "href": "https://dev.azure.com/atarikguney/_apis/GraphProfile/MemberAvatars/msa.YzFhZjAzZjYtNDY0Ny03YWE5LWEwNGQtNzVlMzRlMzE1N2Vh"
133 | }
134 | },
135 | "id": "a3588167-b8df-4070-b04a-b7ce837e699c",
136 | "uniqueName": "atarikguney@gmail.com",
137 | "imageUrl": "https://dev.azure.com/atarikguney/_apis/GraphProfile/MemberAvatars/msa.YzFhZjAzZjYtNDY0Ny03YWE5LWEwNGQtNzVlMzRlMzE1N2Vh",
138 | "descriptor": "msa.YzFhZjAzZjYtNDY0Ny03YWE5LWEwNGQtNzVlMzRlMzE1N2Vh"
139 | },
140 | "System.ChangedDate": "2020-10-16T16:53:54.473Z",
141 | "System.ChangedBy": {
142 | "displayName": "Tarik Guney",
143 | "url": "https://spsprodcus2.vssps.visualstudio.com/A53d7da5a-3070-4ed6-94d8-67304e463b68/_apis/Identities/a3588167-b8df-4070-b04a-b7ce837e699c",
144 | "_links": {
145 | "avatar": {
146 | "href": "https://dev.azure.com/atarikguney/_apis/GraphProfile/MemberAvatars/msa.YzFhZjAzZjYtNDY0Ny03YWE5LWEwNGQtNzVlMzRlMzE1N2Vh"
147 | }
148 | },
149 | "id": "a3588167-b8df-4070-b04a-b7ce837e699c",
150 | "uniqueName": "atarikguney@gmail.com",
151 | "imageUrl": "https://dev.azure.com/atarikguney/_apis/GraphProfile/MemberAvatars/msa.YzFhZjAzZjYtNDY0Ny03YWE5LWEwNGQtNzVlMzRlMzE1N2Vh",
152 | "descriptor": "msa.YzFhZjAzZjYtNDY0Ny03YWE5LWEwNGQtNzVlMzRlMzE1N2Vh"
153 | },
154 | "System.AuthorizedAs": {
155 | "displayName": "Tarik Guney",
156 | "url": "https://spsprodcus2.vssps.visualstudio.com/A53d7da5a-3070-4ed6-94d8-67304e463b68/_apis/Identities/a3588167-b8df-4070-b04a-b7ce837e699c",
157 | "_links": {
158 | "avatar": {
159 | "href": "https://dev.azure.com/atarikguney/_apis/GraphProfile/MemberAvatars/msa.YzFhZjAzZjYtNDY0Ny03YWE5LWEwNGQtNzVlMzRlMzE1N2Vh"
160 | }
161 | },
162 | "id": "a3588167-b8df-4070-b04a-b7ce837e699c",
163 | "uniqueName": "atarikguney@gmail.com",
164 | "imageUrl": "https://dev.azure.com/atarikguney/_apis/GraphProfile/MemberAvatars/msa.YzFhZjAzZjYtNDY0Ny03YWE5LWEwNGQtNzVlMzRlMzE1N2Vh",
165 | "descriptor": "msa.YzFhZjAzZjYtNDY0Ny03YWE5LWEwNGQtNzVlMzRlMzE1N2Vh"
166 | },
167 | "System.PersonId": 72169719,
168 | "System.Watermark": 23,
169 | "System.CommentCount": 0,
170 | "System.Title": "Create the profile edit page",
171 | "System.BoardColumn": "New",
172 | "System.BoardColumnDone": false,
173 | "Microsoft.VSTS.Scheduling.StoryPoints": 2.0,
174 | "Microsoft.VSTS.Common.StateChangeDate": "2020-10-14T16:04:41.533Z",
175 | "Microsoft.VSTS.Common.Priority": 2,
176 | "Microsoft.VSTS.Common.StackRank": 1999955279.0,
177 | "Microsoft.VSTS.Common.ValueArea": "Business",
178 | "WEF_CD17F1AD5A3A4147BAE15B6D7B4EBF5A_System.ExtensionMarker": true,
179 | "WEF_CD17F1AD5A3A4147BAE15B6D7B4EBF5A_Kanban.Column": "New",
180 | "WEF_CD17F1AD5A3A4147BAE15B6D7B4EBF5A_Kanban.Column.Done": false,
181 | "System.Description": "