Welcome and thank you for your interest in contributing to TeamsFX SDK! Before contributing to this project, please review this document for policies and procedures which will ease the contribution and review process for everyone. If you have questions, please raise your issue on github.
Make sure you have installed following prerequisites. To test the SDK, you'd better install Visual Studio Code and TeamsFx VS Code Extension.
- Git
- Node 18 or higher (Node 18 is recommended)
- PNPM 8 or higher
- Clone this repo locally. (
git clone https://github.com/OfficeDev/TeamsFx.git
) - Open a terminal and move into your local copy. (
cd TeamsFx
) - Because the monorepo is managed by PNPM, you need to setup at the first time. (
npm run setup
orpnpm install && npm run build
in root folder) All dependencies will be installed.
Use pnpm-pack to test the SDK.
If you use a project created by TeamsFx VS Code extension, remove the @microsoft/teamsfx
in package.json first.
cd packages/sdk # go into the SDK package directory
pnpm pack # create local packed file
cd ../test-project-using-sdk # go into test project directory.
npm install ../microsoft-teamsfx-x.x.x.tgz # install the local built package
Run npm run build
and npm pack
under packages/sdk
after any updates of SDK.
run pnpm install XXX
to add dependency, run pnpm remove XXX
to remove dependency.
All tests are under test/
folder. The filename ends with ".spec.ts".
E2E tests need environment variables that are configured in GitHub Action. To run SDK E2E tests locally, environment variables are required to be set manually in the .env
file under packages/sdk
.
- Use
npm run test:unit
to run all unit tests. - Use
npm run test:e2e
to run all e2e tests.
- test/unit/: unit tests for both browser and NodeJS.
- test/unit/browser/: unit tests for browser only.
- test/unit/node/: unit tests for NodeJS only.
- test/e2e/browser/: end-to-end tests related to Azure resources for browser only.
- test/e2e/node/: end-to-end tests related to Azure resources for NodeJS only.
All the environment variables are extracted by the function extractIntegrationEnvVariables()
defined in helper.browser.ts
and helper.ts
under test/
folder.
- Create an Azure SQL database.
- Set the environment variable
SDK_INTEGRATION_TEST_SQL
with the Azure SQL Database you just created, and the configs are separated by semicolons.
# SQL related (NodeJS)
SDK_INTEGRATION_TEST_SQL = {SQL_ENDPOINT};{SQL_DATABASE_NAME};{SQL_USER_NAME};{SQL_PASSWORD}
-
Open Teams Toolkit, and sign into M365 by clicking the
Sign in to M365
under theACCOUNTS
section from sidebar with your test account. After you signed in, create a new TeamsSSO-enabled tab
app. Start debugging the project by hitting theF5
key in the Visual Studio Code. -
Set the environment variable
SDK_INTEGRATION_TEST_ACCOUNT
with the test account name and password, separated by semicolons.# AAD Account (NodeJS & browser) SDK_INTEGRATION_TEST_ACCOUNT = {TEST_ACCOUNT_NAME};{TEST_ACCOUNT_PASSWORD}
-
Open Azure Portal with your test account, go to
App Registrations
underAzure Active Directory
, and find the app created and debugged in the previous section. -
Click on the app name, and then go to
Authentication
under theManage
section from sidebar. -
Uncheck Access tokens and check ID tokens under the
Implicit grant and hybrid flows
section. SetAllow public client flows
to Yes. -
Go to
API Permissions
under theManage
section from sidebar, and clickadd a permission
. SelectMicrosoft Graph
API and then selectDelegated permissions
. Scroll down to the bottom and expandUser
, and checkUser.Read.All
. Permission ofUser.Read.All
requires Admin consent. -
Navigate to
Overview
,AAD_TENANT_ID
andAAD_CLIENT_ID
can be found underEssentials
section. -
AAD_AUTHORITY_HOST
ishttps://login.microsoftonline.com
. -
AAD_CLIENT_SECRET
can be found under.fx/states/local.userdata
. ClickDecrypt secret
to get the value. -
Follow the link to find
USER_OBJECT_ID
. -
To setup the certificate, execute following commands to generate self signed certificates for test. The command requires OpenSSL.
# Skip the Export Password with ENTER openssl req -x509 -newkey rsa:4096 -keyout PrivateKey.pem -out Cert.pem -days 365 -nodes -subj "//CN=SdkIntegrationTest" openssl pkcs12 -export -out keyStore.pfx -inkey PrivateKey.pem -in Cert.pem openssl pkcs12 -in keyStore.pfx -out keyStore.pem -nodes
-
In the directory that executes step 9's commands, execute following js script.
AAD_CERTIFICATE_CONTENT
is the contents ofcertificate.txt
.const fs = require("fs"); var cert_key = fs.readFileSync("keyStore.pem", "utf8"); cert_key = cert_key.replace(/\r/g, ""); fs.writeFileSync("certificate.txt", JSON.stringify(cert_key));
-
Go to
Certificates & secrets
of the app underApp registrations
, and uploadCert.pem
by clickingUpload certificate
underCertificate
section. -
Set the environment variable
SDK_INTEGRATION_TEST_AAD
with the following sequence, separated by semicolons.# AAD Application (NodeJS & browser) SDK_INTEGRATION_TEST_AAD = {AAD_AUTHORITY_HOST};{AAD_TENANT_ID};{USER_OBJECT_ID};{AAD_CLIENT_ID};{AAD_CLIENT_SECRET};{AAD_CERTIFICATE_CONTENT}
-
Execute following commands to generate self signed certificates for test. The command requires OpenSSL.
# Generate certs to for test server that supports certificate auth openssl req -x509 -newkey rsa:4096 -nodes -days 365 -keyout server_key.pem -out server_cert.pem -subj "//CN=localhost" # Generate client cert (both PEM and PFX) to test certificate auth support. Press "ENTER" when asked for "Export Password". openssl req -newkey rsa:4096 -keyout client_key.pem -out client_csr.pem -nodes -days 365 -subj "//CN=test client" openssl x509 -req -in client_csr.pem -CA server_cert.pem -CAkey server_key.pem -out client_cert.pem -set_serial 01 -days 365 openssl pkcs12 -inkey client_key.pem -in client_cert.pem -certfile server_cert.pem -export -out client_pfx.pfx # Create password protected private key file openssl rsa -aes256 -in client_key.pem -out client_key_encrypted.pem # Create password protected pfx file. Record the cert password for use in step 2. openssl pkcs12 -inkey client_key.pem -in client_cert.pem -certfile server_cert.pem -export -out client_pfx_encrypted.pfx
-
In the directory that executes step 1's commands, execute following js script to generate values to be configured to the environment variable. You need to fill in the cert password you provided in step 1 before running the script.
const fs = require("fs"); let certs = {}; certs.serverCert = fs.readFileSync("server_cert.pem", "utf8"); certs.serverKey = fs.readFileSync("server_key.pem", "utf8"); certs.clientCert = fs.readFileSync("client_cert.pem", "utf8"); certs.clientKey = fs.readFileSync("client_key.pem", "utf8"); certs.clientKeyEncrypted = fs.readFileSync("client_key_encrypted.pem", "utf8"); certs.clientPfx = Buffer.from(fs.readFileSync("client_pfx.pfx")).toString("base64"); certs.clientPfxEncrypted = Buffer.from(fs.readFileSync("client_pfx_encrypted.pfx")).toString( "base64" ); certs.passphrase = ""; // fill your password before executing the script certs.clientCN = "test client"; // update the value if you specifies another CN when generating test certs fs.writeFileSync("certs.json", JSON.stringify(certs));
-
Copy the contents of
certs.json
to environment variableSDK_INTEGRATION_TEST_API_CERTPROVIDER
.
This project use eslint and prettier to check format and code style.
Use npm run format
to fix format issues and npm run lint
to check lint issues.
- Check out a new branch from "dev".
- Update code in correct place. Supporting Browser and NodeJS
- Make sure modified codes are covered by unit tests. Running test cases
- Ensure code style check has no error. Style Guidelines
- Add comment for public class/method. Please check comment template for details.
- Merge your changes to "dev" branch.
- At least one approve from code owners is required.
- If a new class behaves differently under two environments. Create a new file named xxx.browser.ts that works only in browser and xxx.ts that works only in NodeJS.
- Export files in
index.ts
andindex.browser.ts
, they are entries for NodeJS and browser. - Keep the exported functions and public ones of class consistent in 2 files.
For example:
// onBehalfOfUserCredential.browser.ts
export class OnBehalfOfUserCredential implements TokenCredential {
...
async getToken(
scopes: string | string[],
options?: GetTokenOptions
): Promise<AccessToken | null> {
// browser version implementation.
}
}
// onBehalfOfUserCredential.ts
export class OnBehalfOfUserCredential implements TokenCredential {
...
async getToken(
scopes: string | string[],
options?: GetTokenOptions
): Promise<AccessToken | null> {
// nodejs version implementation.
}
}
Please check onBehalfOfUserCredential.browser.ts and onBehalfOfUserCredential.ts to see the details.