diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..e38d7a2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,70 @@ +name: Bug report +description: Report a problem you're experiencing +labels: bug,unconfirmed,low priority +body: + - type: markdown + attributes: + value: | + Before opening a bug report, please search the existing issues (both open and closed). + + --- + + Thank you for taking the time to file a bug report. To address this bug as fast as possible, we need some information. + - type: input + id: package-version + attributes: + label: Package Version + description: Please provide the full version of the package you have installed. + placeholder: v1.0.0 + validations: + required: true + - type: input + id: laravel-version + attributes: + label: Laravel Version + description: Please provide the full Laravel version of your project. + placeholder: v10.0.0 + validations: + required: true + - type: input + id: php-version + attributes: + label: PHP Version + description: Please provide the full PHP version of your server. + placeholder: PHP 8.3.0 + validations: + required: true + - type: textarea + id: description + attributes: + label: Problem description + description: What happened when you experienced the problem? + validations: + required: true + - type: textarea + id: expectation + attributes: + label: Expected behavior + description: What did you expect to happen instead? + validations: + required: true + - type: textarea + id: steps + attributes: + label: Steps to reproduce + description: Which steps do we need to take to reproduce the problem? Any code examples need to be **as short as possible**, remove any code that is unrelated to the bug. **This issue will be automatically closed and not reviewed if detailed replication steps are missing.** + validations: + required: true + - type: input + id: reproduction + attributes: + label: Reproduction repository + description: The URL of a public GitHub repository which reproduces the problem. **Please do not link to your actual project**, what we need instead is a _minimal_ reproduction in a fresh project without any unnecessary code. This means it doesn\'t matter if your real project is private / confidential, since we want a link to a separate, isolated reproduction. This allows us to fix the problem much quicker. **This issue will be automatically closed and not reviewed if this is missing. Please make sure to format the URL starting with `https://github.com` - only repositories hosted on GitHub are accepted.** + validations: + required: true + - type: textarea + id: logs + attributes: + label: Relevant log output + description: If applicable, provide relevant log output. No need for backticks here. + render: shell diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..baf7446 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Feature request + url: https://github.com/joelbutcher/laravel-facebook-graph/issues/new + about: Share ideas for new features + - name: Support question + url: https://github.com/joelbutcher/laravel-facebook-graph/issues/new + about: Ask the community for help diff --git a/.github/workflows/fix-code-styling.yml b/.github/workflows/fix-code-styling.yml new file mode 100644 index 0000000..c6158da --- /dev/null +++ b/.github/workflows/fix-code-styling.yml @@ -0,0 +1,13 @@ +name: "Fix Code Styling" + +on: + push: + branches: + - main + +permissions: + contents: write + +jobs: + lint: + uses: laravel/.github/.github/workflows/coding-standards.yml@main diff --git a/.github/workflows/manage-issue.yml b/.github/workflows/manage-issue.yml new file mode 100644 index 0000000..acb6649 --- /dev/null +++ b/.github/workflows/manage-issue.yml @@ -0,0 +1,196 @@ +name: manage-issue + +on: + issues: + types: [opened, edited] + +jobs: + check-repro: + runs-on: ubuntu-latest + steps: + - uses: actions/github-script@v3 + with: + script: | + const URL_REGEXP = /### Reproduction repository[\r\n]+([^#]+)###/m + const REPRO_STEPS_REGEXP = /### Steps to reproduce[\r\n]+([^#]+)###/m + const LABEL_NEEDS_MORE_INFORMATION = 'needs more info' + + function debug(...args) { + core.info(args.map(JSON.stringify).join(' ')) + } + + if (context.payload.comment) { + debug('Ignoring comment update.') + + return + } + + const user = context.payload.sender.login + const issue = context.payload.issue + const body = issue.body + const urlMatch = body.match(URL_REGEXP) + const reproStepsMatch = body.match(REPRO_STEPS_REGEXP) + const url = urlMatch !== null ? urlMatch[1].trim() : null + const reproSteps = reproStepsMatch !== null ? reproStepsMatch[1].trim() : null + + debug(`Found URL '${url}'`) + debug(`Found repro steps '${reproSteps}'`) + + async function createComment(comment) { + comment = comment + .split('\n') + .map((line) => line.trim()) + .join('\n') + .trim() + + await github.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: comment, + }) + } + + async function getGitHubActionComments() { + debug(`Loading existing comments...`) + + const comments = await github.issues.listComments({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + }) + + return comments.data.filter(comment => { + debug(`comment by user: '${comment.user.login}'`) + return comment.user.login === 'github-actions[bot]' + }) + } + + async function getIssueLabels() { + const issues = await github.issues.listLabelsOnIssue({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + }) + + return issues.data + } + + async function updateIssue(state, state_reason = null) { + await github.issues.update({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + state, + state_reason, + }) + } + + async function closeWithComment(comment) { + if (issue.state !== 'open') { + debug(`Issue is not open`) + + return + } + + const comments = await getGitHubActionComments() + + if (comments.length > 0) { + debug(`Already commented on issue won't comment again`) + + return + } + + debug(`Missing required information`) + + await github.issues.addLabels({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: [LABEL_NEEDS_MORE_INFORMATION], + }) + + await createComment(comment) + + await updateIssue('closed', 'not_planned') + } + + async function openWithComment(comment) { + if (issue.state !== 'closed') { + debug(`Issue is already open`) + + return + } + + const labels = await getIssueLabels() + const label = labels.find(label => label.name === LABEL_NEEDS_MORE_INFORMATION) + + if (! label) { + debug(`Issue was not tagged as needs information`) + + return + } + + const comments = await getGitHubActionComments() + + if (comments.length === 0) { + debug(`Issue was closed by someone else, won't reopen`) + + return + } + + debug(`Reopening closed issue`) + + await github.issues.removeLabel({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + name: LABEL_NEEDS_MORE_INFORMATION, + }) + + await createComment(comment) + + await updateIssue('open') + } + + const COMMENT_HEADER = ` + Hey @${user}! We're sorry to hear that you've hit this issue. 💙 + `.trim() + + const NO_REPRO_URL = ((! url) || (! url.includes('https://github.com/')) || (url.includes('https://github.com/joelbutcher') && (! url.includes('https://github.com/joelbutcher/laravel-facebook-graph-demo')))) + const NO_REPRO_STEPS = reproSteps.length < 25 + + if (NO_REPRO_URL || NO_REPRO_STEPS) { + let comment = ` + ${COMMENT_HEADER} + + ` + + if (NO_REPRO_URL) { + comment += ` + However, it looks like you forgot to fill in the reproduction repository URL. Can you edit your original post and then we'll look at your issue? + + We need a public GitHub repository which contains a Laravel app with the minimal amount of Socialstream code to reproduce the problem. **Please do not link to your actual project**, what we need instead is a _minimal_ reproduction in a fresh project without any unnecessary code. This means it doesn\'t matter if your real project is private / confidential, since we want a link to a separate, isolated reproduction. That would allow us to download it and review your bug much easier, so it can be fixed quicker. Please make sure to include a database seeder with everything we need to set the app up quickly. + ` + } + + if (NO_REPRO_URL && NO_REPRO_STEPS) { + comment += ` + + Also, ` + } else if (NO_REPRO_STEPS) { + comment += ` + + However, ` + } + + if (NO_REPRO_STEPS) { + comment += `it doesn't look like you've provided much information on how to replicate the issue. Please edit your original post with clear steps we need to take.` + } + + closeWithComment(comment) + } else { + openWithComment(` + Thank you for providing reproduction steps! Reopening the issue now. + `) + } diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 704d545..84a7a63 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -55,6 +55,10 @@ jobs: tools: composer:v2 coverage: none + - name: Remove Laravel Pint + if: matrix.php == '7.3' || matrix.php == '7.4' || matrix.php == '8.0' + run: composer remove --dev laravel/pint --no-update + - name: Install dependencies run: composer update --prefer-dist --no-interaction --no-progress diff --git a/.github/workflows/update-changelog.yml b/.github/workflows/update-changelog.yml new file mode 100644 index 0000000..1625bda --- /dev/null +++ b/.github/workflows/update-changelog.yml @@ -0,0 +1,9 @@ +name: update changelog + +on: + release: + types: [released] + +jobs: + update: + uses: laravel/.github/.github/workflows/update-changelog.yml@main diff --git a/.styleci.yml b/.styleci.yml deleted file mode 100644 index 1430fee..0000000 --- a/.styleci.yml +++ /dev/null @@ -1,5 +0,0 @@ -php: - preset: laravel - version: 8 - disabled: - - no_unused_imports diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..38bf0d7 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1 @@ +# Release Notes diff --git a/composer.json b/composer.json index 2f732b3..20b2a26 100644 --- a/composer.json +++ b/composer.json @@ -26,6 +26,7 @@ "joelbutcher/facebook-graph-sdk": "^6.1.2" }, "require-dev": { + "laravel/pint": "^1.20", "mockery/mockery": "^1.4.2", "orchestra/testbench": "^6.0|^7.0|^9.0", "pestphp/pest": "^1.21|^2.34", diff --git a/pint.json b/pint.json new file mode 100644 index 0000000..f9ec356 --- /dev/null +++ b/pint.json @@ -0,0 +1,3 @@ +{ + "preset": "per" +} diff --git a/src/FacebookServiceProvider.php b/src/FacebookServiceProvider.php index c5f638e..9be1db0 100644 --- a/src/FacebookServiceProvider.php +++ b/src/FacebookServiceProvider.php @@ -67,7 +67,7 @@ protected function mergeConfig(): void */ protected function getConfigPath(): string { - return __DIR__.'/../config/config.php'; + return __DIR__ . '/../config/config.php'; } /** @@ -90,7 +90,7 @@ protected function registerDefaultHttpClient(): void protected function registerUrlDetectionHandler(): void { $this->app->singleton(UrlDetectionInterface::class, function () { - return new UrlDetectionHandler; + return new UrlDetectionHandler(); }); } diff --git a/tests/FacebookTest.php b/tests/FacebookTest.php index 2aeb66c..ffe3951 100644 --- a/tests/FacebookTest.php +++ b/tests/FacebookTest.php @@ -69,32 +69,32 @@ it('builds default scopes', function () { $redirect = $this->getFacebookMock()->getRedirect(); - $this->assertStringContainsStringIgnoringCase('scope='.urlencode('email,public_profile'), $redirect); + $this->assertStringContainsStringIgnoringCase('scope=' . urlencode('email,public_profile'), $redirect); }); it('replaces duplicate scopes from class config', function () { $redirect = $this->getFacebookMock(['scopes' => ['email', 'public_profile']])->getRedirect(); - $this->assertStringContainsStringIgnoringCase('scope='.urlencode('email,public_profile'), $redirect); + $this->assertStringContainsStringIgnoringCase('scope=' . urlencode('email,public_profile'), $redirect); }); it('replaces duplicate scopes passed to method', function () { $redirect = $this->getFacebookMock()->getRedirect(null, ['email', 'public_profile']); - $this->assertStringContainsStringIgnoringCase('scope='.urlencode('email,public_profile'), $redirect); + $this->assertStringContainsStringIgnoringCase('scope=' . urlencode('email,public_profile'), $redirect); }); it('merges config scopes with default scopes', function () { $redirect = $this->getFacebookMock(['scopes' => ['publish_pages']])->getRedirect(); - $this->assertStringContainsStringIgnoringCase('scope='.urlencode('publish_pages,email,public_profile'), $redirect); + $this->assertStringContainsStringIgnoringCase('scope=' . urlencode('publish_pages,email,public_profile'), $redirect); }); it('merges default scopes with scopes passed to method', function () { $redirect = $this->getFacebookMock()->getRedirect(null, ['publish_pages']); - $this->assertStringContainsStringIgnoringCase('scope='.urlencode('publish_pages,email,public_profile'), $redirect); + $this->assertStringContainsStringIgnoringCase('scope=' . urlencode('publish_pages,email,public_profile'), $redirect); }); it('merges config scopes and scopes passed to the method with default scopes to', function () { $redirect = $this->getFacebookMock(['scopes' => ['publish_pages']])->getRedirect(null, ['publish_video']); - $this->assertStringContainsStringIgnoringCase('scope='.urlencode('publish_pages,publish_video,email,public_profile'), $redirect); + $this->assertStringContainsStringIgnoringCase('scope=' . urlencode('publish_pages,publish_video,email,public_profile'), $redirect); }); it('returns a valid redirect login helper instance', function () { @@ -110,13 +110,13 @@ $redirect = $this->getFacebookMock()->getRedirect(); $this->assertStringContainsStringIgnoringCase('response_type=code', $redirect); $this->assertStringContainsStringIgnoringCase('client_id=123456789', $redirect); - $this->assertStringContainsStringIgnoringCase('redirect_uri='.urlencode('http://invalid.zzz'), $redirect); + $this->assertStringContainsStringIgnoringCase('redirect_uri=' . urlencode('http://invalid.zzz'), $redirect); }); it('returns a valid logout url', function () { $redirect = $this->getFacebookMock()->getLogoutUrl('foo-access-token', 'http://invalid.zzz'); $this->assertStringContainsStringIgnoringCase('access_token=foo-access-token', $redirect); - $this->assertStringContainsStringIgnoringCase('next='.urlencode('http://invalid.zzz'), $redirect); + $this->assertStringContainsStringIgnoringCase('next=' . urlencode('http://invalid.zzz'), $redirect); $this->assertStringContainsStringIgnoringCase('https://www.facebook.com/logout.php', $redirect); }); @@ -125,7 +125,7 @@ $this->assertStringContainsStringIgnoringCase('auth_type=rerequest', $redirect); $this->assertStringContainsStringIgnoringCase('response_type=code', $redirect); $this->assertStringContainsStringIgnoringCase('client_id=123456789', $redirect); - $this->assertStringContainsStringIgnoringCase('redirect_uri='.urlencode('http://invalid.zzz'), $redirect); + $this->assertStringContainsStringIgnoringCase('redirect_uri=' . urlencode('http://invalid.zzz'), $redirect); }); it('a valid re authentication url', function () { @@ -133,14 +133,14 @@ $this->assertStringContainsStringIgnoringCase('auth_type=reauthenticate', $redirect); $this->assertStringContainsStringIgnoringCase('response_type=code', $redirect); $this->assertStringContainsStringIgnoringCase('client_id=123456789', $redirect); - $this->assertStringContainsStringIgnoringCase('redirect_uri='.urlencode('http://invalid.zzz'), $redirect); + $this->assertStringContainsStringIgnoringCase('redirect_uri=' . urlencode('http://invalid.zzz'), $redirect); }); it('gets an access token', function () { $_GET['code'] = 'foo_code'; $_GET['state'] = 'foo_state'; - $persistentDataHandler = new InMemoryPersistentDataHandler; + $persistentDataHandler = new InMemoryPersistentDataHandler(); $persistentDataHandler->set('state', 'foo_state'); /** @var \Facebook\Facebook|\Mockery\MockInterface $base */ diff --git a/tests/Fixtures/FooOAuth2Client.php b/tests/Fixtures/FooOAuth2Client.php index 8aa65da..0d9732c 100644 --- a/tests/Fixtures/FooOAuth2Client.php +++ b/tests/Fixtures/FooOAuth2Client.php @@ -8,6 +8,6 @@ class FooOAuth2Client extends OAuth2Client { public function getAccessTokenFromCode($code, $redirectUri = '', $machineId = null) { - return 'foo_token_from_code|'.$code.'|'.$redirectUri; + return 'foo_token_from_code|' . $code . '|' . $redirectUri; } }