├── .editorconfig
├── .github
└── workflows
│ ├── continuous-integration.yml
│ └── update-changelog.yaml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── composer.json
├── config
└── logsnag.php
├── phpstan.neon.dist
├── phpunit.xml.dist
├── pint.json
├── rector.php
├── renovate.json
├── src
├── Client.php
├── Console
│ └── InstallCommand.php
├── Contracts
│ ├── ClientContract.php
│ └── ResponseContract.php
├── Exceptions
│ └── ApiTokenIsMissing.php
├── Facades
│ └── LogSnag.php
├── LogSnag.php
├── LogSnagServiceProvider.php
├── Resources
│ ├── Identify.php
│ ├── Insight.php
│ └── Log.php
├── Responses
│ ├── Identify.php
│ ├── Insight.php
│ └── Log.php
└── helpers.php
└── tests
├── Console
└── InstallCommandTest.php
├── HelpersTest.php
├── LogSnagServiceProviderTest.php
├── Pest.php
├── Resources
├── IdentifyTest.php
├── InsightTest.php
└── LogTest.php
└── TestCase.php
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | indent_size = 4
7 | indent_style = space
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
14 | [*.{toml,yml,yaml}]
15 | indent_size = 2
16 |
--------------------------------------------------------------------------------
/.github/workflows/continuous-integration.yml:
--------------------------------------------------------------------------------
1 | name: Continuous integration
2 |
3 | on:
4 | pull_request:
5 | push:
6 | branches:
7 | - main
8 |
9 | jobs:
10 | check-static-code:
11 | name: Check static code (PHP ${{ matrix.php-version }})
12 |
13 | runs-on: ubuntu-latest
14 |
15 | strategy:
16 | matrix:
17 | php-version: [ 8.1, 8.2, 8.3 ]
18 |
19 | steps:
20 | - name: Checkout
21 | uses: actions/checkout@v3
22 |
23 | - name: Setup PHP
24 | uses: shivammathur/setup-php@v2
25 | with:
26 | coverage: none
27 | php-version: ${{ matrix.php-version }}
28 | tools: cs2pr
29 |
30 | - name: Get composer cache directory
31 | id: composer-cache
32 | run: |
33 | echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
34 |
35 | - name: Restore composer cache
36 | uses: actions/cache@v3
37 | with:
38 | path: ${{ steps.composer-cache.outputs.dir }}
39 | key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
40 | restore-keys: |
41 | ${{ runner.os }}-composer-
42 |
43 | - name: Install composer dependencies
44 | run: composer install --no-progress
45 |
46 | - name: Run PHPStan
47 | run: vendor/bin/phpstan analyse --error-format=checkstyle | cs2pr
48 |
49 | check-code-quality:
50 | name: Check code quality (PHP ${{ matrix.php-version }})
51 |
52 | runs-on: ubuntu-latest
53 |
54 | strategy:
55 | matrix:
56 | php-version: [ 8.1, 8.2, 8.3 ]
57 |
58 | steps:
59 | - name: Checkout
60 | uses: actions/checkout@v3
61 |
62 | - name: Setup PHP
63 | uses: shivammathur/setup-php@v2
64 | with:
65 | coverage: none
66 | php-version: ${{ matrix.php-version }}
67 |
68 | - name: Get composer cache directory
69 | id: composer-cache
70 | run: |
71 | echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
72 |
73 | - name: Restore composer cache
74 | uses: actions/cache@v3
75 | with:
76 | path: ${{ steps.composer-cache.outputs.dir }}
77 | key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
78 | restore-keys: |
79 | ${{ runner.os }}-composer-
80 |
81 | - name: Install composer dependencies
82 | run: composer install --no-progress
83 |
84 | - name: Run Rector
85 | run: vendor/bin/rector --ansi --dry-run
86 |
87 | check-code-style:
88 | name: Check code style (PHP ${{ matrix.php-version }})
89 |
90 | runs-on: ubuntu-latest
91 |
92 | strategy:
93 | matrix:
94 | php-version: [ 8.1, 8.2, 8.3 ]
95 |
96 | steps:
97 | - name: Checkout
98 | uses: actions/checkout@v3
99 |
100 | - name: Setup PHP
101 | uses: shivammathur/setup-php@v2
102 | with:
103 | coverage: none
104 | php-version: ${{ matrix.php-version }}
105 | tools: cs2pr
106 |
107 | - name: Install composer dependencies
108 | run: composer install --no-progress
109 |
110 | - name: Run Pint
111 | id: pint-test
112 | run: vendor/bin/pint --test
113 |
114 | - name: Run Pint with annotations
115 | if: failure() && steps.pint-test.outcome != 'success'
116 | run: vendor/bin/pint --format=checkstyle --test | cs2pr
117 |
118 | tests:
119 | name: PHP tests (PHP ${{ matrix.php-version }})
120 |
121 | runs-on: ubuntu-latest
122 |
123 | strategy:
124 | matrix:
125 | php-version: [ 8.1, 8.2, 8.3 ]
126 |
127 | steps:
128 | - name: Checkout
129 | uses: actions/checkout@v3
130 |
131 | - name: Setup PHP
132 | uses: shivammathur/setup-php@v2
133 | with:
134 | coverage: none
135 | php-version: ${{ matrix.php-version }}
136 |
137 | - name: Get composer cache directory
138 | id: composer-cache
139 | run: |
140 | echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
141 |
142 | - name: Restore composer cache
143 | uses: actions/cache@v3
144 | with:
145 | path: ${{ steps.composer-cache.outputs.dir }}
146 | key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
147 | restore-keys: |
148 | ${{ runner.os }}-composer-
149 |
150 | - name: Install composer dependencies
151 | run: composer install --no-progress
152 |
153 | - name: Run tests
154 | run: composer test
155 |
--------------------------------------------------------------------------------
/.github/workflows/update-changelog.yaml:
--------------------------------------------------------------------------------
1 | name: Update Changelog
2 |
3 | on:
4 | release:
5 | types: [ released ]
6 |
7 | jobs:
8 | update-changelog:
9 | name: Update Changelog
10 |
11 | runs-on: ubuntu-latest
12 |
13 | permissions:
14 | contents: write
15 |
16 | steps:
17 | - name: Checkout
18 | uses: actions/checkout@v3
19 | with:
20 | fetch-depth: 0
21 | ref: ${{ github.event.release.target_commitish }}
22 |
23 | - name: Extract release date from git tag
24 | id: release_date
25 | run: |
26 | echo "date=$(git log -1 --date=short --format=%ad '${{ github.event.release.tag_name }}')" >> $GITHUB_OUTPUT;
27 |
28 | - name: Update Changelog
29 | uses: stefanzweifel/changelog-updater-action@v1
30 | with:
31 | release-date: ${{ steps.release_date.outputs.date }}
32 | release-notes: ${{ github.event.release.body }}
33 | latest-version: ${{ github.event.release.tag_name }}
34 | parse-github-usernames: true
35 |
36 | - name: Commit updated CHANGELOG
37 | uses: stefanzweifel/git-auto-commit-action@v4
38 | with:
39 | branch: ${{ github.event.release.target_commitish }}
40 | commit_message: Update CHANGELOG
41 | file_pattern: CHANGELOG.md
42 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 |
3 | vendor
4 |
5 | composer.lock
6 | phpunit.xml
7 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file.
4 |
5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 |
8 | ## [Unreleased](https://github.com/hosmelq/laravel-logsnag/compare/v0.4.0...HEAD)
9 |
10 | ## [v0.4.0](https://github.com/hosmelq/laravel-logsnag/compare/v0.3.1...v0.4.0) - 2024-04-09
11 |
12 | ### Added
13 |
14 | - Laravel 11 support.
15 |
16 | ## [v0.3.1](https://github.com/hosmelq/laravel-logsnag/compare/v0.3.0...v0.3.1) - 2023-09-12
17 |
18 | ### Fixed
19 |
20 | - Add missing `user_id` to the `log`.
21 |
22 | ## [v0.3.0](https://github.com/hosmelq/laravel-logsnag/compare/v0.2.0...v0.3.0) - 2023-09-12
23 |
24 | ### Added
25 |
26 | - Support [Identify](https://docs.logsnag.com/endpoints/identify)
27 |
28 | ## [v0.2.0](https://github.com/hosmelq/laravel-logsnag/compare/v0.1.5...v0.2.0) - 2023-08-14
29 |
30 | ### Removed
31 |
32 | - Support for Laravel 9
33 |
34 | ## [v0.1.5](https://github.com/hosmelq/laravel-logsnag/compare/v0.1.4...v0.1.5) - 2023-08-14
35 |
36 | ### Fixed
37 |
38 | - Fix a condition that checks if the project name is present in the payload.
39 |
40 | ## [v0.1.4](https://github.com/hosmelq/laravel-logsnag/compare/v0.1.3...v0.1.4) - 2023-08-13
41 |
42 | ### Fixed
43 |
44 | - Fix types.
45 | - Fix import function.
46 |
47 | ## [v0.1.3](https://github.com/hosmelq/laravel-logsnag/compare/v0.1.2...v0.1.3) - 2023-08-13
48 |
49 | ### Added
50 |
51 | - Add `logsnag` helper function.
52 |
53 | ### Fixed
54 |
55 | - Fix types.
56 |
57 | ## [v0.1.2](https://github.com/hosmelq/laravel-logsnag/compare/v0.1.1...v0.1.2) - 2023-08-13
58 |
59 | ### Added
60 |
61 | - Add install command.
62 |
63 | ## [v0.1.1](https://github.com/hosmelq/laravel-logsnag/compare/v0.1.0...v0.1.1) - 2023-08-13
64 |
65 | ### Fixed
66 |
67 | - Fix PHP version range in composer.json
68 |
69 | ## [v0.1.0](https://github.com/hosmelq/laravel-logsnag/releases/tag/v0.1.0) - 2023-08-13
70 |
71 | - Initial pre-release
72 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Hosmel Quintana
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | > This is an unofficial package for [LogSnag](https://logsnag.com).
2 |
3 | # Laravel LogSnag
4 |
5 | Easily integrate [LogSnag](https://logsnag.com)'s event tracking into your Laravel app. Monitor user activity,
6 | get realtime analytics, and receive instant insights.
7 |
8 | ## Requirements
9 |
10 | This package requires PHP 8.1 or higher and Laravel 10.0 or higher.
11 |
12 | ## Installation
13 |
14 | You can install the package via composer:
15 |
16 | ```sh
17 | composer require hosmelq/laravel-logsnag
18 | ```
19 |
20 | ## Configuration
21 |
22 | You can publish the config file with:
23 |
24 | ```sh
25 | php artisan vendor:publish --tag="logsnag-config"
26 | ```
27 |
28 | Alternatively, you may utilize the install command.
29 |
30 | ```sh
31 | php artisan logsnag:install
32 | ```
33 |
34 | Next, you should configure your LogSnag API token and project name in your application’s `.env` file:
35 |
36 | ```dosini
37 | LOGSNAG_API_TOKEN=your-api-token
38 | LOGSNAG_PROJECT=your-project-name
39 | ```
40 |
41 | We determine the project to send events to using `LOGSNAG_PROJECT`. Alternatively, you can specify
42 | the project name when creating an event or insight.
43 |
44 | ## Usage
45 |
46 | ### Publish Event
47 |
48 | You can use the `LogSnag` facade to publish events, and insights.
49 |
50 | ```php
51 | use HosmelQ\LogSnag\Laravel\Facades\LogSnag;
52 |
53 | LogSnag::log()->publish([
54 | 'channel' => 'waitlist',
55 | 'event' => 'User Joined Waitlist',
56 | 'icon' => '🎉',
57 | ]);
58 | ```
59 |
60 | This method returns a [Log](src/Responses/Log.php) instance that provides access to the response
61 | from the LogSnag API. For more information and a list of available parameters, refer to the LogSnag
62 | [documentation](https://docs.logsnag.com/endpoints/log) for Log.
63 |
64 | ## Publish Insight
65 |
66 | ```php
67 | use HosmelQ\LogSnag\Laravel\Facades\LogSnag;
68 |
69 | LogSnag::insight()->publish([
70 | 'icon' => '🎉',
71 | 'title' => 'Registered Users on Waitlist',
72 | 'value' => 8,
73 | ]);
74 | ```
75 |
76 | This method returns an [Insight](src/Responses/Log.php) instance that provides access to the response
77 | from the LogSnag API. For more information and a list of available parameters, refer to the LogSnag
78 | [documentation](https://docs.logsnag.com/endpoints/insight) for Insight.
79 |
80 | ## User Properties
81 |
82 | ```php
83 | use HosmelQ\LogSnag\Laravel\Facades\LogSnag;
84 |
85 | LogSnag::identify()->publish([
86 | 'properties' => [
87 | 'email' => 'john@doe.com',
88 | 'name' => 'John Doe',
89 | 'plan' => 'premium',
90 | ],
91 | 'user_id' => '123',
92 | ]);
93 | ```
94 |
95 | This method returns an [Identify](src/Responses/Identify.php) instance that provides access to the response
96 | from the LogSnag API. For more information and a list of available parameters, refer to the LogSnag
97 | [documentation](https://docs.logsnag.com/endpoints/identify) for Identify.
98 |
99 | Apart from the facade, you can utilize the `logsnag` helper function.
100 |
101 | ```php
102 | logsnag()->log()->publish([
103 | 'channel' => 'waitlist',
104 | 'event' => 'User Joined Waitlist',
105 | 'icon' => '🎉',
106 | ]);
107 | ```
108 |
109 | ## Credits
110 |
111 | - [Hosmel Quintana](https://github.com/hosmelq)
112 | - [All Contributors](../../contributors)
113 |
114 | ## License
115 |
116 | The MIT License (MIT). Please see [License File](LICENSE) for more information.
117 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hosmelq/laravel-logsnag",
3 | "description": "Integrate the power of LogSnag's real-time event tracking into your Laravel application.",
4 | "license": "MIT",
5 | "type": "library",
6 | "keywords": [
7 | "hosmelq",
8 | "laravel",
9 | "logsnag",
10 | "laravel-logsnag",
11 | "log",
12 | "logging"
13 | ],
14 | "authors": [
15 | {
16 | "name": "Hosmel Quintana",
17 | "email": "hosmelq@gmail.com"
18 | }
19 | ],
20 | "require": {
21 | "php": "^8.1 || ^8.2 || ^8.3",
22 | "guzzlehttp/guzzle": "^7.7",
23 | "laravel/framework": "^10.0.0 || ^11.0.0"
24 | },
25 | "require-dev": {
26 | "larastan/larastan": "^2.9",
27 | "laravel/pint": "^1.10",
28 | "orchestra/testbench": "^7.0 || ^8.0 || ^9.0",
29 | "pestphp/pest": "^2.13",
30 | "pestphp/pest-plugin-laravel": "^2.2",
31 | "phpstan/phpstan": "^1.10",
32 | "phpstan/phpstan-deprecation-rules": "^1.1",
33 | "rector/rector": "^1.0.0",
34 | "thecodingmachine/phpstan-safe-rule": "^1.2"
35 | },
36 | "minimum-stability": "stable",
37 | "prefer-stable": true,
38 | "autoload": {
39 | "psr-4": {
40 | "HosmelQ\\LogSnag\\Laravel\\": "src/"
41 | },
42 | "files": [
43 | "src/helpers.php"
44 | ]
45 | },
46 | "autoload-dev": {
47 | "psr-4": {
48 | "HosmelQ\\LogSnag\\Laravel\\Tests\\": "tests/"
49 | }
50 | },
51 | "config": {
52 | "allow-plugins": {
53 | "pestphp/pest-plugin": true
54 | },
55 | "sort-packages": true
56 | },
57 | "extra": {
58 | "laravel": {
59 | "aliases": {
60 | "LogSnag": "HosmelQ\\LogSnag\\Facade"
61 | },
62 | "providers": [
63 | "HosmelQ\\LogSnag\\Laravel\\LogSnagServiceProvider"
64 | ]
65 | }
66 | },
67 | "scripts": {
68 | "post-autoload-dump": [
69 | "@php vendor/bin/testbench package:discover --ansi"
70 | ],
71 | "analyse": "phpstan analyse --configuration=phpstan.neon.dist",
72 | "rector": "rector",
73 | "test": "pest"
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/config/logsnag.php:
--------------------------------------------------------------------------------
1 | env('LOGSNAG_API_TOKEN'),
18 |
19 | /*
20 | |--------------------------------------------------------------------------
21 | | LogSnag Project Identifier
22 | |--------------------------------------------------------------------------
23 | |
24 | | Specify the unique identifier for your LogSnag project. It's used
25 | | to associate events with a specific project within your LogSnag account.
26 | | You can find this identifier in the project settings of your LogSnag dashboard.
27 | |
28 | */
29 |
30 | 'project' => env('LOGSNAG_PROJECT'),
31 |
32 | ];
33 |
--------------------------------------------------------------------------------
/phpstan.neon.dist:
--------------------------------------------------------------------------------
1 | includes:
2 | - vendor/larastan/larastan/extension.neon
3 | - vendor/phpstan/phpstan-deprecation-rules/rules.neon
4 | - vendor/thecodingmachine/phpstan-safe-rule/phpstan-safe-rule.neon
5 |
6 | parameters:
7 | checkBenevolentUnionTypes: true
8 | checkModelProperties: true
9 | checkOctaneCompatibility: true
10 | level: 9
11 |
12 | paths:
13 | - src
14 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 | ./tests/
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/pint.json:
--------------------------------------------------------------------------------
1 | {
2 | "preset": "psr12",
3 | "rules": {
4 | "array_indentation": true,
5 | "binary_operator_spaces": {
6 | "default": "single_space"
7 | },
8 | "cast_spaces": true,
9 | "class_attributes_separation": true,
10 | "concat_space": true,
11 | "declare_strict_types": true,
12 | "lambda_not_used_import": true,
13 | "method_chaining_indentation": true,
14 | "multiline_whitespace_before_semicolons": true,
15 | "no_extra_blank_lines": {
16 | "tokens": [
17 | "extra",
18 | "throw",
19 | "use"
20 | ]
21 | },
22 | "no_trailing_comma_in_singleline_array": true,
23 | "no_unneeded_import_alias": true,
24 | "no_unused_imports": true,
25 | "no_whitespace_before_comma_in_array": true,
26 | "not_operator_with_successor_space": true,
27 | "object_operator_without_whitespace": true,
28 | "ordered_imports": {
29 | "imports_order": [
30 | "const",
31 | "function",
32 | "class"
33 | ],
34 | "sort_algorithm": "alpha"
35 | },
36 | "single_import_per_statement": false,
37 | "single_quote": true,
38 | "trailing_comma_in_multiline": {
39 | "elements": ["arrays"]
40 | },
41 | "trim_array_spaces": true,
42 | "unary_operator_spaces": true,
43 | "whitespace_after_comma_in_array": true
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/rector.php:
--------------------------------------------------------------------------------
1 | paths([
14 | __DIR__.'/src',
15 | __DIR__.'/tests',
16 | ]);
17 |
18 | $config->rules([
19 | InlineConstructorDefaultToPropertyRector::class,
20 | ]);
21 |
22 | $config->sets([
23 | LevelSetList::UP_TO_PHP_81,
24 | SetList::CODE_QUALITY,
25 | SetList::DEAD_CODE,
26 | SetList::EARLY_RETURN,
27 | SetList::TYPE_DECLARATION,
28 | ]);
29 |
30 | $config->skip([
31 | ClosureToArrowFunctionRector::class,
32 | // Disable this rule for now, as it's causing issues with the Laravel
33 | // service providers type-hinting $app as array.
34 | StrictArrayParamDimFetchRector::class => [
35 | __DIR__.'/src/LogSnagServiceProvider.php',
36 | ],
37 | ]);
38 | };
39 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "automerge": true,
4 | "enabledManagers": ["composer", "github-actions"],
5 | "extends": ["config:base", "schedule:weekly"],
6 | "labels": ["dependencies"],
7 | "lockFileMaintenance": {
8 | "enabled": false
9 | },
10 | "major": {
11 | "automerge": false
12 | },
13 | "packageRules": [
14 | {
15 | "addLabels": ["composer"],
16 | "groupName": "Composer",
17 | "matchManagers": ["composer"]
18 | },
19 | {
20 | "addLabels": ["github-actions"],
21 | "groupName": "GitHub Actions",
22 | "matchManagers": ["github-actions"]
23 | }
24 | ],
25 | "rangeStrategy": "update-lockfile",
26 | "rebaseWhen": "conflicted"
27 | }
28 |
--------------------------------------------------------------------------------
/src/Client.php:
--------------------------------------------------------------------------------
1 | client);
22 | }
23 |
24 | public function insight(): Insight
25 | {
26 | return new Insight($this->client);
27 | }
28 |
29 | public function log(): Log
30 | {
31 | return new Log($this->client);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Console/InstallCommand.php:
--------------------------------------------------------------------------------
1 | callSilently('vendor:publish', [
20 | '--tag' => 'logsnag-config',
21 | ]);
22 |
23 | if ($this->confirm('Would you like to star the repo on GitHub?')) {
24 | $url = 'https://github.com/hosmelq/laravel-logsnag';
25 |
26 | $command = [
27 | 'Darwin' => 'open',
28 | 'Linux' => 'xdg-open',
29 | 'Windows' => 'start',
30 | ][PHP_OS_FAMILY] ?? null;
31 |
32 | if ($command) {
33 | exec("$command $url");
34 | }
35 | }
36 |
37 | $this->info('LogSnag has been installed!');
38 |
39 | return self::SUCCESS;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/Contracts/ClientContract.php:
--------------------------------------------------------------------------------
1 | httpClient
25 | ->acceptJson()
26 | ->asJson()
27 | ->baseUrl($this->baseUri)
28 | ->withToken($this->apiToken);
29 |
30 | return new Client($this->httpClient);
31 | }
32 |
33 | public function withApiToken(string $token): static
34 | {
35 | $this->apiToken = trim($token);
36 |
37 | return $this;
38 | }
39 |
40 | public function withHttpClient(PendingRequest $client): static
41 | {
42 | $this->httpClient = $client;
43 |
44 | return $this;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/LogSnagServiceProvider.php:
--------------------------------------------------------------------------------
1 | mergeConfigFrom(
18 | __DIR__.'/../config/logsnag.php',
19 | 'logsnag'
20 | );
21 |
22 | $this->app->singleton(ClientContract::class, function ($app): Client {
23 | $config = $app['config']['logsnag'];
24 |
25 | if (! is_string($apiToken = $config['api_token'])) {
26 | throw ApiTokenIsMissing::create();
27 | }
28 |
29 | return LogSnag::factory()
30 | ->withApiToken($apiToken)
31 | ->withHttpClient(Http::throw())
32 | ->make();
33 | });
34 |
35 | $this->app->alias(ClientContract::class, 'logsnag');
36 | }
37 |
38 | public function boot(): void
39 | {
40 | if ($this->app->runningInConsole()) {
41 | $this->commands([
42 | Console\InstallCommand::class,
43 | ]);
44 |
45 | $this->publishes([
46 | __DIR__.'/../config/logsnag.php' => config_path('logsnag.php'),
47 | ], 'logsnag-config');
48 | }
49 | }
50 |
51 | /**
52 | * @return string[]
53 | */
54 | public function provides(): array
55 | {
56 | return [
57 | 'logsnag',
58 | ClientContract::class,
59 | ];
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/Resources/Identify.php:
--------------------------------------------------------------------------------
1 | client
27 | ->post('identify', $payload)
28 | ->json();
29 |
30 | return IdentifyResponse::from($response);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Resources/Insight.php:
--------------------------------------------------------------------------------
1 | client
27 | ->post('insight', $payload)
28 | ->json();
29 |
30 | return InsightResponse::from($response);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Resources/Log.php:
--------------------------------------------------------------------------------
1 | , user_id?: string} $payload
18 | */
19 | public function publish(array $payload): LogResponse
20 | {
21 | if (! array_key_exists('project', $payload)) {
22 | $payload['project'] = config('logsnag.project');
23 | }
24 |
25 | /** @var array{channel: string, description?: string, event: string, icon?: string, notify?: bool, parse?: 'markdown'|'text', project: string, tags?: array, user_id?: string} $response */
26 | $response = $this->client
27 | ->post('log', $payload)
28 | ->json();
29 |
30 | return LogResponse::from($response);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Responses/Identify.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | class Identify implements ResponseContract
13 | {
14 | /**
15 | * @param array{string: bool|numeric|string} $properties
16 | */
17 | public function __construct(
18 | public string $project,
19 | public array $properties,
20 | public string $userId,
21 | ) {
22 | }
23 |
24 | /**
25 | * @param array{project: string, properties: array{string: bool|numeric|string}, user_id: string} $attributes
26 | */
27 | public static function from(array $attributes): self
28 | {
29 | return new self(
30 | project: $attributes['project'],
31 | properties: $attributes['properties'],
32 | userId: $attributes['user_id'],
33 | );
34 | }
35 |
36 | /**
37 | * {@inheritDoc}
38 | */
39 | public function toArray(): array
40 | {
41 | return [
42 | 'project' => $this->project,
43 | 'properties' => $this->properties,
44 | 'user_id' => $this->userId,
45 | ];
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Responses/Insight.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | class Insight implements ResponseContract
13 | {
14 | public function __construct(
15 | public string $project,
16 | public string $title,
17 | public float|int|string $value,
18 | public ?string $icon,
19 | ) {
20 | }
21 |
22 | /**
23 | * @param array{icon?: string, project: string, title: string, value: numeric|string} $attributes
24 | */
25 | public static function from(array $attributes): self
26 | {
27 | return new self(
28 | project: $attributes['project'],
29 | title: $attributes['title'],
30 | value: $attributes['value'],
31 | icon: $attributes['icon'] ?? null,
32 | );
33 | }
34 |
35 | /**
36 | * {@inheritDoc}
37 | */
38 | public function toArray(): array
39 | {
40 | return [
41 | 'icon' => $this->icon,
42 | 'project' => $this->project,
43 | 'title' => $this->title,
44 | 'value' => $this->value,
45 | ];
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Responses/Log.php:
--------------------------------------------------------------------------------
1 | , user_id: string|null}>
11 | */
12 | class Log implements ResponseContract
13 | {
14 | /**
15 | * @param array $tags
16 | * @param 'markdown'|'text' $parse
17 | */
18 | public function __construct(
19 | public string $channel,
20 | public string $event,
21 | public string $project,
22 | public ?string $description,
23 | public ?string $icon,
24 | public ?string $userId,
25 | public bool $notify = false,
26 | public string $parse = 'text',
27 | public array $tags = [],
28 | ) {
29 | }
30 |
31 | /**
32 | * @param array{channel: string, description?: string, event: string, icon?: string, notify?: bool, parse?: 'markdown'|'text', project: string, tags?: array, user_id?: string} $attributes
33 | */
34 | public static function from(array $attributes): self
35 | {
36 | return new self(
37 | channel: $attributes['channel'],
38 | event: $attributes['event'],
39 | project: $attributes['project'],
40 | description: $attributes['description'] ?? null,
41 | icon: $attributes['icon'] ?? null,
42 | userId: $attributes['user_id'] ?? null,
43 | notify: $attributes['notify'] ?? false,
44 | parse: $attributes['parse'] ?? 'text',
45 | tags: $attributes['tags'] ?? [],
46 | );
47 | }
48 |
49 | public function toArray(): array
50 | {
51 | return [
52 | 'channel' => $this->channel,
53 | 'description' => $this->description,
54 | 'event' => $this->event,
55 | 'icon' => $this->icon,
56 | 'notify' => $this->notify,
57 | 'parse' => $this->parse,
58 | 'project' => $this->project,
59 | 'tags' => $this->tags,
60 | 'user_id' => $this->userId,
61 | ];
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/helpers.php:
--------------------------------------------------------------------------------
1 | expectsConfirmation('Would you like to star the repo on GitHub?')
10 | ->assertSuccessful();
11 |
12 | expect(config_path('logsnag.php'))->toBeFile();
13 | });
14 |
--------------------------------------------------------------------------------
/tests/HelpersTest.php:
--------------------------------------------------------------------------------
1 | toBeInstanceOf(ClientContract::class);
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/tests/LogSnagServiceProviderTest.php:
--------------------------------------------------------------------------------
1 | set('logsnag.api_token', null);
9 |
10 | resolve('logsnag');
11 | })->throws(
12 | ApiTokenIsMissing::class,
13 | 'LogSnag API token is missing. Please set the LOGSNAG_API_TOKEN environment variable.',
14 | );
15 |
16 | it('binds the client as singleton', function (): void {
17 | $client = resolve('logsnag');
18 |
19 | expect(resolve('logsnag'))->toBe($client);
20 | });
21 |
--------------------------------------------------------------------------------
/tests/Pest.php:
--------------------------------------------------------------------------------
1 | in(__DIR__);
8 |
--------------------------------------------------------------------------------
/tests/Resources/IdentifyTest.php:
--------------------------------------------------------------------------------
1 | Http::response([
12 | 'properties' => [
13 | 'name' => 'John Doe',
14 | ],
15 | 'project' => 'test-project',
16 | 'user_id' => '123',
17 | ]),
18 | ]);
19 |
20 | /** @var \HosmelQ\LogSnag\Laravel\Client $client */
21 | $client = resolve('logsnag');
22 |
23 | $insight = $client->identify()->publish([
24 | 'properties' => [
25 | 'name' => 'John Doe',
26 | ],
27 | 'user_id' => '123',
28 | ]);
29 |
30 | expect($insight)
31 | ->toBeInstanceOf(Identify::class)
32 | ->project->toBe('test-project')
33 | ->properties->toBe([
34 | 'name' => 'John Doe',
35 | ])
36 | ->userId->toBe('123');
37 | });
38 |
39 | it('can create an identify with custom project', function (): void {
40 | Http::fake([
41 | '*/identify' => Http::response([
42 | 'properties' => [
43 | 'name' => 'John Doe',
44 | ],
45 | 'project' => 'test-project',
46 | 'user_id' => '123',
47 | ]),
48 | ]);
49 |
50 | /** @var \HosmelQ\LogSnag\Laravel\Client $client */
51 | $client = resolve('logsnag');
52 |
53 | $client->identify()->publish([
54 | 'project' => 'test-project-2',
55 | 'properties' => [
56 | 'name' => 'John Doe',
57 | ],
58 | 'user_id' => '123',
59 | ]);
60 |
61 | Http::assertSent(function (Request $request): bool {
62 | return $request->data()['project'] == 'test-project-2';
63 | });
64 | });
65 |
--------------------------------------------------------------------------------
/tests/Resources/InsightTest.php:
--------------------------------------------------------------------------------
1 | Http::response([
12 | 'project' => 'test-project',
13 | 'title' => 'Registered Users on Waitlist',
14 | 'value' => 1,
15 | ]),
16 | ]);
17 |
18 | /** @var \HosmelQ\LogSnag\Laravel\Client $client */
19 | $client = resolve('logsnag');
20 |
21 | $insight = $client->insight()->publish([
22 | 'title' => 'Registered Users on Waitlist',
23 | 'value' => 1,
24 | ]);
25 |
26 | expect($insight)
27 | ->toBeInstanceOf(Insight::class)
28 | ->icon->toBeNull()
29 | ->project->toBe('test-project')
30 | ->title->toBe('Registered Users on Waitlist')
31 | ->value->toBe(1);
32 |
33 | Http::assertSent(function (Request $request): bool {
34 | return $request->data()['project'] == 'test-project';
35 | });
36 | });
37 |
38 | it('can create an insight with all information', function (): void {
39 | Http::fake([
40 | '*/insight' => Http::response([
41 | 'icon' => '🎉',
42 | 'project' => 'test-project',
43 | 'title' => 'Registered Users on Waitlist',
44 | 'value' => 1,
45 | ]),
46 | ]);
47 |
48 | /** @var \HosmelQ\LogSnag\Laravel\Client $client */
49 | $client = resolve('logsnag');
50 |
51 | $insight = $client->insight()->publish([
52 | 'icon' => '🎉',
53 | 'title' => 'Registered Users on Waitlist',
54 | 'value' => 1,
55 | ]);
56 |
57 | expect($insight)
58 | ->toBeInstanceOf(Insight::class)
59 | ->icon->toBe('🎉')
60 | ->project->toBe('test-project')
61 | ->title->toBe('Registered Users on Waitlist')
62 | ->value->toBe(1);
63 | });
64 |
65 | it('can create an insight with custom project', function (): void {
66 | Http::fake([
67 | '*/insight' => Http::response([
68 | 'project' => 'test-project-2',
69 | 'title' => 'Registered Users on Waitlist',
70 | 'value' => 1,
71 | ]),
72 | ]);
73 |
74 | /** @var \HosmelQ\LogSnag\Laravel\Client $client */
75 | $client = resolve('logsnag');
76 |
77 | $client->insight()->publish([
78 | 'project' => 'test-project-2',
79 | 'title' => 'Registered Users on Waitlist',
80 | 'value' => 1,
81 | ]);
82 |
83 | Http::assertSent(function (Request $request): bool {
84 | return $request->data()['project'] == 'test-project-2';
85 | });
86 | });
87 |
--------------------------------------------------------------------------------
/tests/Resources/LogTest.php:
--------------------------------------------------------------------------------
1 | Http::response([
12 | 'project' => 'test-project',
13 | 'channel' => 'waitlist',
14 | 'event' => 'User Joined Waitlist',
15 | 'notify' => false,
16 | 'parser' => 'text',
17 | ]),
18 | ]);
19 |
20 | /** @var \HosmelQ\LogSnag\Laravel\Client $client */
21 | $client = resolve('logsnag');
22 |
23 | $log = $client->log()->publish([
24 | 'channel' => 'waitlist',
25 | 'event' => 'User Joined Waitlist',
26 | ]);
27 |
28 | expect($log)
29 | ->toBeInstanceOf(Log::class)
30 | ->channel->toBe('waitlist')
31 | ->description->toBeNull()
32 | ->event->toBe('User Joined Waitlist')
33 | ->icon->toBeNull()
34 | ->notify->toBeFalse()
35 | ->parse->toBe('text')
36 | ->project->toBe('test-project')
37 | ->tags->toBe([]);
38 |
39 | Http::assertSent(function (Request $request): bool {
40 | return $request->data()['project'] == 'test-project';
41 | });
42 | });
43 |
44 | it('can create a log with all information', function (): void {
45 | Http::fake([
46 | '*/log' => Http::response([
47 | 'channel' => 'waitlist',
48 | 'description' => 'Email: hosmelq@gmail.com',
49 | 'event' => 'User Joined Waitlist',
50 | 'icon' => '🎉',
51 | 'notify' => true,
52 | 'parse' => 'text',
53 | 'project' => 'test-project',
54 | 'tags' => [
55 | 'email' => 'hosmelq@gmail.com',
56 | 'name' => 'hosmel quintana',
57 | ],
58 | 'user_id' => '123',
59 | ]),
60 | ]);
61 |
62 | /** @var \HosmelQ\LogSnag\Laravel\Client $client */
63 | $client = resolve('logsnag');
64 |
65 | $log = $client->log()->publish([
66 | 'channel' => 'waitlist',
67 | 'description' => 'Email: hosmelq@gmail.com',
68 | 'event' => 'User Joined Waitlist',
69 | 'icon' => '🎉',
70 | 'notify' => true,
71 | 'parse' => 'text',
72 | 'tags' => [
73 | 'email' => 'hosmelq@gmail.com',
74 | 'name' => 'Hosmel Quintana',
75 | ],
76 | 'user_id' => '123',
77 | ]);
78 |
79 | expect($log)
80 | ->toBeInstanceOf(Log::class)
81 | ->channel->toBe('waitlist')
82 | ->description->toBe('Email: hosmelq@gmail.com')
83 | ->event->toBe('User Joined Waitlist')
84 | ->icon->toBe('🎉')
85 | ->notify->toBeTrue()
86 | ->parse->toBe('text')
87 | ->project->toBe('test-project')
88 | ->tags->toBe([
89 | 'email' => 'hosmelq@gmail.com',
90 | 'name' => 'hosmel quintana',
91 | ])
92 | ->userId->toBe('123');
93 | });
94 |
95 | it('can create a log with custom project', function (): void {
96 | Http::fake([
97 | '*/log' => Http::response([
98 | 'project' => 'test-project-2',
99 | 'channel' => 'waitlist',
100 | 'event' => 'User Joined Waitlist',
101 | ]),
102 | ]);
103 |
104 | /** @var \HosmelQ\LogSnag\Laravel\Client $client */
105 | $client = resolve('logsnag');
106 |
107 | $client->log()->publish([
108 | 'channel' => 'waitlist',
109 | 'event' => 'User Joined Waitlist',
110 | 'project' => 'test-project-2',
111 | ]);
112 |
113 | Http::assertSent(function (Request $request): bool {
114 | return $request->data()['project'] == 'test-project-2';
115 | });
116 | });
117 |
--------------------------------------------------------------------------------
/tests/TestCase.php:
--------------------------------------------------------------------------------
1 | set('logsnag', [
23 | 'api_token' => 'api-token',
24 | 'project' => 'test-project',
25 | ]);
26 | }
27 |
28 | protected function getPackageProviders($app): array
29 | {
30 | return [
31 | LogSnagServiceProvider::class,
32 | ];
33 | }
34 | }
35 |
--------------------------------------------------------------------------------